diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2018-04-07 13:05:13 -0700 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2018-04-07 13:06:15 -0700 |
commit | 91fadfaedf04241ff71b512b1bb9b3ebad45b607 (patch) | |
tree | d6ef74f28b8bb9e69e5f5d6c008f127dc19dd30a /.emacs.d/initlibs | |
parent | 3fe038ceef923df58adaf4e06df7962c078ad03c (diff) | |
download | dotfiles-91fadfaedf04241ff71b512b1bb9b3ebad45b607.tar.gz |
.emacs.d/{lisp => initlibs}
dirs called 'lisp' has a special meaning in Emacs
Diffstat (limited to '.emacs.d/initlibs')
-rw-r--r-- | .emacs.d/initlibs/bind-key.el | 413 | ||||
-rw-r--r-- | .emacs.d/initlibs/diminish.el | 294 | ||||
-rw-r--r-- | .emacs.d/initlibs/key-chord.el | 372 | ||||
-rw-r--r-- | .emacs.d/initlibs/let-alist.el | 170 | ||||
-rw-r--r-- | .emacs.d/initlibs/smex.el | 446 | ||||
-rw-r--r-- | .emacs.d/initlibs/use-package.el | 1194 |
6 files changed, 2889 insertions, 0 deletions
diff --git a/.emacs.d/initlibs/bind-key.el b/.emacs.d/initlibs/bind-key.el new file mode 100644 index 00000000..995e4816 --- /dev/null +++ b/.emacs.d/initlibs/bind-key.el @@ -0,0 +1,413 @@ +;;; bind-key.el --- A simple way to manage personal keybindings + +;; Copyright (c) 2012-2015 john wiegley + +;; Author: John Wiegley <jwiegley@gmail.com> +;; Maintainer: John Wiegley <jwiegley@gmail.com> +;; Created: 16 Jun 2012 +;; Version: 1.0 +;; Keywords: keys keybinding config dotemacs +;; 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 2, 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 gnu emacs; see the file copying. if not, write to the +;; free software foundation, inc., 59 temple place - suite 330, +;; boston, ma 02111-1307, usa. + +;;; 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 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 overriden 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. + +(require 'cl-lib) +(require 'easy-mmode) + +(defgroup bind-key nil + "A simple way to manage personal keybindings" + :group 'emacs) + +(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]-\\)?\\|.+-\\)" + "Regular expression used to divide key sets in the output from +\\[describe-personal-keybindings]." + :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) + "override-global-mode keymap") + +(define-minor-mode override-global-mode + "A minor mode so that keymap settings override other modes." + t "") + +;; 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 documentation of +`edmacro-mode' for details. + +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")) + (kdescvar (make-symbol "kdesc")) + (bindingvar (make-symbol "binding"))) + `(let* ((,namevar ,key-name) + (,keyvar (if (vectorp ,namevar) ,namevar + (read-kbd-macro ,namevar))) + (,kdescvar (cons (if (stringp ,namevar) ,namevar + (key-description ,namevar)) + (quote ,keymap))) + (,bindingvar (lookup-key (or ,keymap global-map) ,keyvar))) + (add-to-list 'personal-keybindings + (list ,kdescvar ,command + (unless (numberp ,bindingvar) ,bindingvar))) + ,(if predicate + `(define-key (or ,keymap global-map) ,keyvar + '(menu-item "" nil :filter (lambda (&optional _) + (when ,predicate + ,command)))) + `(define-key (or ,keymap global-map) ,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." + `(progn + (bind-key ,key-name nil ,keymap) + (setq personal-keybindings + (cl-delete-if #'(lambda (k) + ,(if keymap + `(and (consp (car k)) + (string= (caar k) ,key-name) + (eq (cdar k) ',keymap)) + `(and (stringp (car k)) + (string= (car k) ,key-name)))) + personal-keybindings)))) + +;;;###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) + "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 +:filter FORM - optional form to determine when bindings apply + +The rest of the arguments are conses of keybinding string and a +function symbol (unquoted)." + ;; jww (2016-02-26): This is a hack; this whole function needs to be + ;; rewritten to normalize arguments the way that use-package.el does. + (if (and (eq (car args) :package) + (not (eq (car (cdr (cdr args))) :map))) + (setq args (cons :map (cons 'global-map args)))) + (let* ((map (plist-get args :map)) + (doc (plist-get args :prefix-docstring)) + (prefix-map (plist-get args :prefix-map)) + (prefix (plist-get args :prefix)) + (filter (plist-get args :filter)) + (menu-name (plist-get args :menu-name)) + (pkg (plist-get args :package)) + (key-bindings (progn + (while (keywordp (car args)) + (pop args) + (pop args)) + args))) + (when (or (and prefix-map (not prefix)) + (and prefix (not prefix-map))) + (error "Both :prefix-map and :prefix must be supplied")) + (when (and menu-name (not prefix)) + (error "If :menu-name is supplied, :prefix must be too")) + (let ((args key-bindings) + saw-map 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 (eq map 'global-map))) + (if (boundp map) + bindings + `((eval-after-load + ,(if (symbolp pkg) `',pkg pkg) + '(progn ,@bindings)))) + bindings))) + (append + (when prefix-map + `((defvar ,prefix-map) + ,@(when doc `((put ',prefix-map 'variable-documentation ,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))))) + (wrap map + (cl-mapcan + (lambda (form) + (if prefix-map + `((bind-key ,(car form) ',(cdr form) ,prefix-map ,filter)) + (if (and map (not (eq map 'global-map))) + `((bind-key ,(car form) ',(cdr form) ,map ,filter)) + `((bind-key ,(car form) ',(cdr form) nil ,filter))))) + first)) + (when next + (bind-keys-form + (if pkg + (cons :package (cons pkg next)) + next)))))))) + +;;;###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 +: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))) + +;;;###autoload +(defmacro bind-keys* (&rest args) + (macroexp-progn + (bind-keys-form `(:map override-global-map ,@args)))) + +(defun get-binding-description (elem) + (cond + ((listp elem) + (cond + ((eq 'lambda (car elem)) + (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\n%s\n\n" + (cdar 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: +;; indent-tabs-mode: nil +;; End: + +;;; bind-key.el ends here diff --git a/.emacs.d/initlibs/diminish.el b/.emacs.d/initlibs/diminish.el new file mode 100644 index 00000000..ae92ea8e --- /dev/null +++ b/.emacs.d/initlibs/diminish.el @@ -0,0 +1,294 @@ +;;; diminish.el --- Diminished modes are minor modes with no modeline display + +;; Copyright (C) 1998 Free Software Foundation, Inc. + +;; Author: Will Mengarini <seldon@eskimo.com> +;; URL: <http://www.eskimo.com/~seldon> +;; Package-Version: 0.44 +;; Created: Th 19 Feb 98 +;; Version: 0.44, Sa 23 Jan 99 +;; Keywords: extensions, diminish, minor, codeprose + +;; This file is part of GNU Emacs. + +;; 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 2, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Minor modes each put a word on the mode line to signify that they're +;; active. This can cause other displays, such as % of file that point is +;; at, to run off the right side of the screen. For some minor modes, such +;; as mouse-avoidance-mode, the display is a waste of space, since users +;; typically set the mode in their .emacs & never change it. For other +;; modes, such as my jiggle-mode, it's a waste because there's already a +;; visual indication of whether the mode is in effect. + +;; A diminished mode is a minor mode that has had its mode line +;; display diminished, usually to nothing, although diminishing to a +;; shorter word or a single letter is also supported. This package +;; implements diminished modes. + +;; You can use this package either interactively or from your .emacs file. +;; In either case, first you'll need to copy this file to a directory that +;; appears in your load-path. `load-path' is the name of a variable that +;; contains a list of directories Emacs searches for files to load. +;; To prepend another directory to load-path, put a line like +;; (add-to-list 'load-path "c:/My_Directory") in your .emacs file. + +;; To create diminished modes interactively, type +;; M-x load-library +;; to get a prompt like +;; Load library: +;; and respond `diminish' (unquoted). Then type +;; M-x diminish +;; to get a prompt like +;; Diminish what minor mode: +;; and respond with the name of some minor mode, like mouse-avoidance-mode. +;; You'll then get this prompt: +;; To what mode-line display: +;; Respond by just hitting <Enter> if you want the name of the mode +;; completely removed from the mode line. If you prefer, you can abbreviate +;; the name. If your abbreviation is 2 characters or more, such as "Av", +;; it'll be displayed as a separate word on the mode line, just like minor +;; modes' names. If it's a single character, such as "V", it'll be scrunched +;; up against the previous word, so for example if the undiminished mode line +;; display had been "Abbrev Fill Avoid", it would become "Abbrev FillV". +;; Multiple single-letter diminished modes will all be scrunched together. +;; The display of undiminished modes will not be affected. + +;; To find out what the mode line would look like if all diminished modes +;; were still minor, type M-x diminished-modes. This displays in the echo +;; area the complete list of minor or diminished modes now active, but +;; displays them all as minor. They remain diminished on the mode line. + +;; To convert a diminished mode back to a minor mode, type M-x diminish-undo +;; to get a prompt like +;; Restore what diminished mode: +;; Respond with the name of some diminished mode. To convert all +;; diminished modes back to minor modes, respond to that prompt +;; with `diminished-modes' (unquoted, & note the hyphen). + +;; When you're responding to the prompts for mode names, you can use +;; completion to avoid extra typing; for example, m o u SPC SPC SPC +;; is usually enough to specify mouse-avoidance-mode. Mode names +;; typically end in "-mode", but for historical reasons +;; auto-fill-mode is named by "auto-fill-function". + +;; To create diminished modes noninteractively in your .emacs file, put +;; code like +;; (require 'diminish) +;; (diminish 'abbrev-mode "Abv") +;; (diminish 'jiggle-mode) +;; (diminish 'mouse-avoidance-mode "M") +;; near the end of your .emacs file. It should be near the end so that any +;; minor modes your .emacs loads will already have been loaded by the time +;; they're to be converted to diminished modes. + +;; To diminish a major mode, (setq mode-name "whatever") in the mode hook. + +;;; Epigraph: + +;; "The quality of our thoughts is bordered on all sides +;; by our facility with language." +;; --J. Michael Straczynski + +;;; Code: + +(eval-when-compile (require 'cl)) + +(defvar diminish-must-not-copy-minor-mode-alist nil + "Non-nil means loading diminish.el won't (copy-alist minor-mode-alist). +Normally `minor-mode-alist' is setq to that copy on loading diminish because +at least one of its cons cells, that for abbrev-mode, is read-only (see +ELisp Info on \"pure storage\"). If you setq this variable to t & then +try to diminish abbrev-mode under GNU Emacs 19.34, you'll get the error +message \"Attempt to modify read-only object\".") + +(or diminish-must-not-copy-minor-mode-alist + (callf copy-alist minor-mode-alist)) + +(defvar diminished-mode-alist nil + "The original `minor-mode-alist' value of all (diminish)ed modes.") + +(defvar diminish-history-symbols nil + "Command history for symbols of diminished modes.") + +(defvar diminish-history-names nil + "Command history for names of diminished modes.") + +;; When we diminish a mode, we are saying we want it to continue doing its +;; work for us, but we no longer want to be reminded of it. It becomes a +;; night worker, like a janitor; it becomes an invisible man; it remains a +;; component, perhaps an important one, sometimes an indispensable one, of +;; the mechanism that maintains the day-people's world, but its place in +;; their thoughts is diminished, usually to nothing. As we grow old we +;; diminish more and more such thoughts, such people, usually to nothing. + +;; "The wise man knows that to keep under is to endure." The diminished +;; often come to value their invisibility. We speak--speak--of "the strong +;; silent type", but only as a superficiality; a stereotype in a movie, +;; perhaps, but even if an acquaintance, necessarily, by hypothesis, a +;; distant one. The strong silent type is actually a process. It begins +;; with introspection, continues with judgment, and is shaped by the +;; discovery that these judgments are impractical to share; there is no +;; appetite for the wisdom of the self-critical among the creatures of +;; material appetite who dominate our world. Their dominance's Darwinian +;; implications reinforce the self-doubt that is the germ of higher wisdom. +;; The thoughtful contemplate the evolutionary triumph of the predator. +;; Gnostics deny the cosmos could be so evil; this must all be a prank; the +;; thoughtful remain silent, invisible, self-diminished, and discover, +;; perhaps at first in surprise, the freedom they thus gain, and grow strong. + +;;;###autoload +(defun diminish (mode &optional to-what) + "Diminish mode-line display of minor mode MODE to TO-WHAT (default \"\"). + +Interactively, enter (with completion) the name of any minor mode, followed +on the next line by what you want it diminished to (default empty string). +The response to neither prompt should be quoted. However, in Lisp code, +both args must be quoted, the first as a symbol, the second as a string, +as in (diminish 'jiggle-mode \" Jgl\"). + +The mode-line displays of minor modes usually begin with a space, so +the modes' names appear as separate words on the mode line. However, if +you're having problems with a cramped mode line, you may choose to use single +letters for some modes, without leading spaces. Capitalizing them works +best; if you then diminish some mode to \"X\" but have abbrev-mode enabled as +well, you'll get a display like \"AbbrevX\". This function prepends a space +to TO-WHAT if it's > 1 char long & doesn't already begin with a space." + (interactive (list (read (completing-read + "Diminish what minor mode: " + (mapcar (lambda (x) (list (symbol-name (car x)))) + minor-mode-alist) + nil t nil 'diminish-history-symbols)) + (read-from-minibuffer + "To what mode-line display: " + nil nil nil 'diminish-history-names))) + (let ((minor (assq mode minor-mode-alist))) + (or minor (error "%S is not currently registered as a minor mode" mode)) + (callf or to-what "") + (when (> (length to-what) 1) + (or (= (string-to-char to-what) ?\ ) + (callf2 concat " " to-what))) + (or (assq mode diminished-mode-alist) + (push (copy-sequence minor) diminished-mode-alist)) + (setcdr minor (list to-what)))) + +;; But an image comes to me, vivid in its unreality, of a loon alone on his +;; forest lake, shrieking his soul out into a canopy of stars. Alone this +;; afternoon in my warm city apartment, I can feel the bite of his night air, +;; and smell his conifers. In him there is no acceptance of diminishment. + +;; "I have a benevolent habit of pouring out myself to everybody, +;; and would even pay for a listener, and I am afraid +;; that the Athenians may think me too talkative." +;; --Socrates, in the /Euthyphro/ + +;; I remember a news story about a retired plumber who had somehow managed to +;; steal a military tank. He rode it down city streets, rode over a parked +;; car--no one was hurt--rode onto a freeway, that concrete symbol of the +;; American spirit, or so we fancy it, shouting "Plumber Bob! Plumber Bob!". +;; He was shot dead by police. + +;;;###autoload +(defun diminish-undo (mode) + "Restore mode-line display of diminished mode MODE to its minor-mode value. +Do nothing if the arg is a minor mode that hasn't been diminished. + +Interactively, enter (with completion) the name of any diminished mode (a +mode that was formerly a minor mode on which you invoked M-x diminish). +To restore all diminished modes to minor status, answer `diminished-modes'. +The response to the prompt shouldn't be quoted. However, in Lisp code, +the arg must be quoted as a symbol, as in (diminish-undo 'diminished-modes)." + (interactive + (list (read (completing-read + "Restore what diminished mode: " + (cons (list "diminished-modes") + (mapcar (lambda (x) (list (symbol-name (car x)))) + diminished-mode-alist)) + nil t nil 'diminish-history-symbols)))) + (if (eq mode 'diminished-modes) + (let ((diminished-modes diminished-mode-alist)) + (while diminished-modes + (diminish-undo (caar diminished-modes)) + (callf cdr diminished-modes))) + (let ((minor (assq mode minor-mode-alist)) + (diminished (assq mode diminished-mode-alist))) + (or minor + (error "%S is not currently registered as a minor mode" mode)) + (when diminished + (setcdr minor (cdr diminished)))))) + +;; Plumber Bob was not from Seattle, my grey city, for rainy Seattle is a +;; city of interiors, a city of the self-diminished. When I moved here one +;; sunny June I was delighted to find that ducks and geese were common in +;; the streets. But I hoped to find a loon or two, and all I found were +;; ducks and geese. I wondered about this; I wondered why there were no +;; loons in Seattle; but my confusion resulted from my ignorance of the +;; psychology of rain, which is to say my ignorance of diminished modes. +;; What I needed, and lacked, was a way to discover they were there. + +;;;###autoload +(defun diminished-modes () + "Echo all active diminished or minor modes as if they were minor. +The display goes in the echo area; if it's too long even for that, +you can see the whole thing in the *Messages* buffer. +This doesn't change the status of any modes; it just lets you see +what diminished modes would be on the mode-line if they were still minor." + (interactive) + (let ((minor-modes minor-mode-alist) + message) + (while minor-modes + (when (symbol-value (caar minor-modes)) + ;; This minor mode is active in this buffer + (let* ((mode-pair (car minor-modes)) + (mode (car mode-pair)) + (minor-pair (or (assq mode diminished-mode-alist) mode-pair)) + (minor-name (cadr minor-pair))) + (when (symbolp minor-name) + ;; This minor mode uses symbol indirection in the cdr + (let ((symbols-seen (list minor-name))) + (while (and (symbolp (callf symbol-value minor-name)) + (not (memq minor-name symbols-seen))) + (push minor-name symbols-seen)))) + (push minor-name message))) + (callf cdr minor-modes)) + (setq message (mapconcat 'identity (nreverse message) "")) + (when (= (string-to-char message) ?\ ) + (callf substring message 1)) + (message "%s" message))) + +;; A human mind is a Black Forest of diminished modes. Some are dangerous; +;; most of the mind of an intimate is a secret stranger, and these diminished +;; modes are rendered more unpredictable by their long isolation from the +;; corrective influence of interaction with reality. The student of history +;; learns that this description applies to whole societies as well. In some +;; ways the self-diminished are better able to discern the night worker. +;; They are rendered safer by their heightened awareness of others' +;; diminished modes, and more congenial by the spare blandness of their own +;; mode lines. To some people rain is truly depressing, but others it just +;; makes pensive, and, forcing them indoors where they may not have the +;; luxury of solitude, teaches them to self-diminish. That was what I had +;; not understood when I was searching for loons among the ducks and geese. +;; Loons come to Seattle all the time, but the ones that like it learn to be +;; silent, learn to self-diminish, and take on the colors of ducks and geese. +;; Now, here a dozen years, I can recognize them everywhere, standing quietly +;; in line with the ducks and geese at the espresso counter, gazing placidly +;; out on the world through loon-red eyes, thinking secret thoughts. + +(provide 'diminish) + +;;; diminish.el ends here diff --git a/.emacs.d/initlibs/key-chord.el b/.emacs.d/initlibs/key-chord.el new file mode 100644 index 00000000..b5f65d0e --- /dev/null +++ b/.emacs.d/initlibs/key-chord.el @@ -0,0 +1,372 @@ +;;; key-chord.el --- map pairs of simultaneously pressed keys to commands +;;------------------------------------------------------------------- +;; +;; Copyright (C) 2003,2005,2008,2012 David Andersson +;; +;; This file is NOT part of Emacs. +;; +;; 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 2 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, write to the Free +;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +;; MA 02111-1307 USA +;; +;;------------------------------------------------------------------- + +;; Author: David Andersson <l.david.andersson(at)sverige.nu> +;; Created: 27 April 2003 +;; Version: 0.6 (2012-10-23) +;; Keywords: keyboard chord input + +;;; Commentary: + +;; ######## Compatibility ######################################## +;; +;; Works with Emacs-20.3, 20.6, 20.7, 21.2, 21.4, 22.1 and 23.1 +;; Does not work with Emacs-19.31 nor XEmacs-20.4 and 21.4. + +;; ######## Quick start ######################################## +;; +;; Add to your ~/.emacs +;; +;; (require 'key-chord) +;; (key-chord-mode 1) +;; +;; and some chords, for example +;; +;; (key-chord-define-global "hj" 'undo) +;; (key-chord-define-global ",." "<>\C-b") + +;; ######## Terminology ######################################## +;; +;; In this package, a "key chord" is two keys pressed simultaneously, +;; or a single key quickly pressed twice. +;; +;; (Sometimes pressing SHIFT and/or META plus another key is call a chord, +;; but not here. However SHIFT plus two normal keys can be a "key chord".) + +;; ######## Description ######################################## +;; +;; Key chord mode acts like a global minor mode controlled by the function +;; `key-chord-mode'. +;; +;; Key chord definitions are stored in ordinary key-maps. +;; The function `key-chord-define-global' defines a chord in the global +;; key-map and `key-chord-define' defines a chord in a specified key-map, +;; for example for a specific mode. +;; +;; A TWO-key chord is two distinct keys pressed simultaneously (within +;; one tenth of a second, or so). +;; +;; Examples: +;; +;; (key-chord-define-global ",." "<>\C-b") +;; (key-chord-define-global "hj" 'undo) +;; (key-chord-define-global [?h ?j] 'undo) ; the same +;; (key-chord-define-global "jk" 'dabbrev-expand) +;; (key-chord-define-global "cv" 'reindent-then-newline-and-indent) +;; (key-chord-define-global "4r" "$") +;; +;; Comma and dot pressed together insert a pair of angle brackets. +;; `h' and `j' pressed together invoke the undo command. +;; `j' and `k' pressed together invoke the dabbrev-expand command. +;; 'c' and 'v' pressed together insert a newline. +;; `4' and `r' pressed together insert a dollar sign. +;; +;; A ONE-key chord is a single key quickly pressed twice (within one third +;; of a second or so). +;; +;; Examples: +;; +;; (key-chord-define-global "''" "`'\C-b") +;; (key-chord-define-global ",," 'indent-for-comment) +;; (key-chord-define-global "qq" "the ") +;; (key-chord-define-global "QQ" "The ") +;; +;; Tick (') pressed twice inserts a back-tick and a tick (`'). +;; Comma (,) pressed twice indents for and/or inserts a comment. +;; `q' pressed twice inserts the word "the ". +;; +;; Examples: Mode specific chords +;; +;; (key-chord-define c++-mode-map ";;" "\C-e;") +;; (key-chord-define c++-mode-map "{}" "{\n\n}\C-p\t") +;; +;; The command `key-chord-describe' lists currently defined key chords. +;; The standard command `describe-bindings' (C-h b) will also show key chords. +;; +;; The standard command `describe-key' (C-h k) will accept a key chord and +;; show its definition. (Isn't that amazing. There is no explicit code to +;; carry out this functionality.) + +;; ######## Tips ######################################## +;; +;; Don't chord key combinations that exists in the languages you typically +;; write. Otherwise, if you are typing fast, two key intended to be separate +;; letters might instead trig a chord. +;; E.g. "uu" would be a good chord in spanish but not in finnish, and +;; "hj" would be a good chord in english but not in swedish. +;; +;; Don't rely solely on /usr/dict/words to find unusual combination. +;; For example "cv" or "fg" can be quite common in certain kinds of +;; programming. Grep your own texts to verify that a combination is unusual. +;; And don't forget to check both permutations: "fg" and "gf". +;; +;; Choose two keys that are close to each other on the keyboard, so they +;; can be quickly typed without effort. Chords involving two hands (as +;; opposed to two fingers on one hand) are harder to type (quickly). +;; The idea is that key chords are to replace function keys for functions +;; that are frequently performed while the hands are in writing position. +;; +;; Key chords might not work well over a slow network. + +;; ######## Limitations ######################################## +;; +;; When recording keyboard macros, the time between keyboard inputs are not +;; recorded. Thus, the key-chord-input-method cannot know for sure if two keys +;; in a macro was a chord or not. The current solution remembers the first key +;; of the chords typed during macro recording, and keys that match those (and +;; are defined as chords) are considered key-chords during macro execution. +;; This knowledge is not saved with `name-last-kbd-macro', so they may +;; execute wrong if they contain pair of keys that match defined chords. +;; +;; Emacs will not call input-method-function for keys that have non numeric +;; codes or whos code is outside the range 32..126. Thus you cannot define +;; key chords involving function keys, control keys, or even your non-english +;; letters (on national keyboards) that otherwise are well positioned for +;; chording on your keyboard. +;; (I think chording left and right arrow keys would be useful, but cannot do. +;; I consider this a bug in Emacs. Input methods could happily return +;; unmodified *any* key they don't know about.) +;; +;; Key chords longer that 2 keys are not supported. It could be done, but I +;; don't think it is worth the trubbel since most keyboards will not reliably +;; send all key codes when 3 or more keys are pressed simultaneously. +;; It might also be a bit trickier to maintain performance. +;; +;; Key chord mode uses input-method-function. And so do internationalisation +;; packages (mule, quail, etc). Do not expect them to work well together. +;; The last one that gets the input-method-function rules. + +;; ######## Implementation ######################################## +;; +;; Key chords piggy back in ordinary key maps, so they can be defined +;; per mode without having to add hooks to all modes. +;; +;; Key chord key codes are vectors beginning with the atom `key-chord'. +;; A two key chord, e.g. "hj", will add two entries in the key-map. +;; E.g. [key-chord ?h ?j] and [key-chord ?j ?h]. +;; +;; When key-chord-mode is enabled input-method-function is set to +;; key-chord-input-method. + +;; ######## To do ######################################## +;; +;; * Find a way to save key-chord info in keyboard macros. +;; +;; * Save previous value of input-method-function? And call it? +;; +;; * input-method-function is reset in *info* buffers! What to do? +;; +;; * How to enter interactively command OR string in key-chord-define-global? +;; +;; * Customize public vars (defcustom). + +;; ######## History ######################################## +;; +;; 0.6 (2012-10-23) l.david.andersson(at)sverige.nu +;; Add key-chord-define-local, key-chord-unset-local, key-chord-unset-global +;; 0.5 (2008-09-15) david(at)symsoft.se +;; Bugfix sit-for; Improved examples; New E-mail in comment +;; 0.4 (2005-05-07) david(at)symsoft.se +;; Slightly better macro heuristics; Added option key-chord-in-macros +;; 0.3 (2005-04-14) david(at)symsoft.se +;; Require advice; More examples +;; 0.2 (2003-09-13) david(at)symsoft.se +;; Quick and dirty fix for keyboard macros +;; 0.1 (2003-04-27) david(at)symsoft.se +;; First release + +;;; Code: + +(defvar key-chord-two-keys-delay 0.1 ; 0.05 or 0.1 + "Max time delay between two key press to be considered a key chord.") + +(defvar key-chord-one-key-delay 0.2 ; 0.2 or 0.3 to avoid first autorepeat + "Max time delay between two press of the same key to be considered a key chord. +This should normally be a little longer than `key-chord-two-keys-delay'.") + +(defvar key-chord-in-macros t + "If nil, don't expand key chords when executing keyboard macros. +If non-nil, expand chord sequenses in macros, but only if a similar chord was +entered during the last interactive macro recording. (This carries a bit of +guesswork. We can't know for sure when executing whether two keys were +typed quickly or slowly when recorded.)") + +;; Internal vars +(defvar key-chord-mode nil) + +;; Shortcut for key-chord-input-method: no need to test a key again if it +;; didn't matched a chord the last time. Improves feedback during autorepeat. +(defvar key-chord-last-unmatched nil) + +;; Macro heuristics: Keep track of which chords was used when the last macro +;; was defined. Or rather, only the first-char of the chords. Only expand +;; matching chords during macro execution. +(defvar key-chord-in-last-kbd-macro nil) +(defvar key-chord-defining-kbd-macro nil) + +;;;###autoload +(defun key-chord-mode (arg) + "Toggle key chord mode. +With positive ARG enable the mode. With zero or negative arg disable the mode. +A key chord is two keys that are pressed simultaneously, or one key quickly +pressed twice. +\nSee functions `key-chord-define-global', `key-chord-define-local', and +`key-chord-define' and variables `key-chord-two-keys-delay' and +`key-chord-one-key-delay'." + + (interactive "P") + (setq key-chord-mode (if arg + (> (prefix-numeric-value arg) 0) + (not key-chord-mode))) + (cond (key-chord-mode + (setq input-method-function 'key-chord-input-method) + (message "Key Chord mode on")) + (t + (setq input-method-function nil) + (message "Key Chord mode off")))) + +;;;###autoload +(defun key-chord-define-global (keys command) + "Define a key-chord of the two keys in KEYS starting a COMMAND. +\nKEYS can be a string or a vector of two elements. Currently only elements +that corresponds to ascii codes in the range 32 to 126 can be used. +\nCOMMAND can be an interactive function, a string, or nil. +If COMMAND is nil, the key-chord is removed. +\nNote that KEYS defined locally in the current buffer will have precedence." + (interactive "sSet key chord globally (2 keys): \nCSet chord \"%s\" to command: ") + (key-chord-define (current-global-map) keys command)) + +;;;###autoload +(defun key-chord-define-local (keys command) + "Locally define a key-chord of the two keys in KEYS starting a COMMAND. +\nKEYS can be a string or a vector of two elements. Currently only elements +that corresponds to ascii codes in the range 32 to 126 can be used. +\nCOMMAND can be an interactive function, a string, or nil. +If COMMAND is nil, the key-chord is removed. +\nThe binding goes in the current buffer's local map, +which in most cases is shared with all other buffers in the same major mode." + (interactive "sSet key chord locally (2 keys): \nCSet chord \"%s\" to command: ") + (key-chord-define (current-local-map) keys command)) + +(defun key-chord-unset-global (keys) + "Remove global key-chord of the two keys in KEYS." + (interactive "sUnset key chord globally (2 keys): ") + (key-chord-define (current-local-map) keys nil)) + +(defun key-chord-unset-local (keys) + "Remove local key-chord of the two keys in KEYS." + (interactive "sUnset key chord locally (2 keys): ") + (key-chord-define (current-local-map) keys nil)) + +;;;###autoload +(defun key-chord-define (keymap keys command) + "Define in KEYMAP, a key-chord of the two keys in KEYS starting a COMMAND. +\nKEYS can be a string or a vector of two elements. Currently only elements +that corresponds to ascii codes in the range 32 to 126 can be used. +\nCOMMAND can be an interactive function, a string, or nil. +If COMMAND is nil, the key-chord is removed." + (if (/= 2 (length keys)) + (error "Key-chord keys must have two elements")) + ;; Exotic chars in a string are >255 but define-key wants 128..255 for those + (let ((key1 (logand 255 (aref keys 0))) + (key2 (logand 255 (aref keys 1)))) + (if (eq key1 key2) + (define-key keymap (vector 'key-chord key1 key2) command) + ;; else + (define-key keymap (vector 'key-chord key1 key2) command) + (define-key keymap (vector 'key-chord key2 key1) command)))) + +(defun key-chord-lookup-key1 (keymap key) + "Like lookup-key but no third arg and no numeric return value." + (let ((res (lookup-key keymap key))) + (if (numberp res) + nil + ;; else + res))) + +(defun key-chord-lookup-key (key) + "Lookup KEY in all current key maps." + (let ((maps (current-minor-mode-maps)) + res) + (while (and maps (not res)) + (setq res (key-chord-lookup-key1 (car maps) key) + maps (cdr maps))) + (or res + (if (current-local-map) + (key-chord-lookup-key1 (current-local-map) key)) + (key-chord-lookup-key1 (current-global-map) key)))) + +(defun key-chord-describe () + "List key chord bindings in a help buffer. +\nTwo key chords will be listed twice and there will be Prefix Commands. +Please ignore that." + (interactive) + (describe-bindings [key-chord])) + +(defun key-chord-input-method (first-char) + "Input method controlled by key bindings with the prefix `key-chord'." + (if (and (not (eq first-char key-chord-last-unmatched)) + (key-chord-lookup-key (vector 'key-chord first-char))) + (let ((delay (if (key-chord-lookup-key (vector 'key-chord first-char first-char)) + key-chord-one-key-delay + ;; else + key-chord-two-keys-delay))) + (if (if executing-kbd-macro + (not (memq first-char key-chord-in-last-kbd-macro)) + (when (bound-and-true-p eldoc-mode) + (eldoc-pre-command-refresh-echo-area)) + + (sit-for delay 0 'no-redisplay)) + (progn + (setq key-chord-last-unmatched nil) + (list first-char)) + ;; else input-pending-p + (let* ((input-method-function nil) + (next-char (read-event)) + (res (vector 'key-chord first-char next-char))) + (if (key-chord-lookup-key res) + (progn + (setq key-chord-defining-kbd-macro + (cons first-char key-chord-defining-kbd-macro)) + (list 'key-chord first-char next-char)) + ;; else put back next-char and return first-char + (setq unread-command-events (cons next-char unread-command-events)) + (if (eq first-char next-char) + (setq key-chord-last-unmatched first-char)) + (list first-char))))) + ;; else no key-chord keymap + (setq key-chord-last-unmatched first-char) + (list first-char))) + +(require 'advice) + +(defadvice start-kbd-macro (after key-chord activate) + (setq key-chord-defining-kbd-macro nil)) + +(defadvice end-kbd-macro (after key-chord activate) + (setq key-chord-in-last-kbd-macro key-chord-defining-kbd-macro)) + +(provide 'key-chord) + +;;; key-chord.el ends here diff --git a/.emacs.d/initlibs/let-alist.el b/.emacs.d/initlibs/let-alist.el new file mode 100644 index 00000000..dbf31e02 --- /dev/null +++ b/.emacs.d/initlibs/let-alist.el @@ -0,0 +1,170 @@ +;;; let-alist.el --- Easily let-bind values of an assoc-list by their names -*- lexical-binding: t; -*- + +;; Copyright (C) 2014-2015 Free Software Foundation, Inc. + +;; Author: Artur Malabarba <bruce.connor.am@gmail.com> +;; Maintainer: Artur Malabarba <bruce.connor.am@gmail.com> +;; Version: 1.0.4 +;; Keywords: extensions lisp +;; Prefix: let-alist +;; Separator: - + +;; 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 <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package offers a single macro, `let-alist'. This macro takes a +;; first argument (whose value must be an alist) and a body. +;; +;; The macro expands to a let form containing body, where each dotted +;; symbol inside body is let-bound to their cdrs in the alist. Dotted +;; symbol is any symbol starting with a `.'. Only those present in +;; the body are let-bound and this search is done at compile time. +;; +;; For instance, the following code +;; +;; (let-alist alist +;; (if (and .title .body) +;; .body +;; .site +;; .site.contents)) +;; +;; essentially expands to +;; +;; (let ((.title (cdr (assq 'title alist))) +;; (.body (cdr (assq 'body alist))) +;; (.site (cdr (assq 'site alist))) +;; (.site.contents (cdr (assq 'contents (cdr (assq 'site alist)))))) +;; (if (and .title .body) +;; .body +;; .site +;; .site.contents)) +;; +;; If you nest `let-alist' invocations, the inner one can't access +;; the variables of the outer one. You can, however, access alists +;; inside the original alist by using dots inside the symbol, as +;; displayed in the example above by the `.site.contents'. +;; +;;; Code: + + +(defun let-alist--deep-dot-search (data) + "Return alist of symbols inside DATA that start with a `.'. +Perform a deep search and return an alist where each car is the +symbol, and each cdr is the same symbol without the `.'." + (cond + ((symbolp data) + (let ((name (symbol-name data))) + (when (string-match "\\`\\." name) + ;; Return the cons cell inside a list, so it can be appended + ;; with other results in the clause below. + (list (cons data (intern (replace-match "" nil nil name))))))) + ((not (consp data)) nil) + (t (append (let-alist--deep-dot-search (car data)) + (let-alist--deep-dot-search (cdr data)))))) + +(defun let-alist--access-sexp (symbol variable) + "Return a sexp used to access SYMBOL inside VARIABLE." + (let* ((clean (let-alist--remove-dot symbol)) + (name (symbol-name clean))) + (if (string-match "\\`\\." name) + clean + (let-alist--list-to-sexp + (mapcar #'intern (nreverse (split-string name "\\."))) + variable)))) + +(defun let-alist--list-to-sexp (list var) + "Turn symbols LIST into recursive calls to `cdr' `assq' on VAR." + `(cdr (assq ',(car list) + ,(if (cdr list) (let-alist--list-to-sexp (cdr list) var) + var)))) + +(defun let-alist--remove-dot (symbol) + "Return SYMBOL, sans an initial dot." + (let ((name (symbol-name symbol))) + (if (string-match "\\`\\." name) + (intern (replace-match "" nil nil name)) + symbol))) + + +;;; The actual macro. +;;;###autoload +(defmacro let-alist (alist &rest body) + "Let-bind dotted symbols to their cdrs in ALIST and execute BODY. +Dotted symbol is any symbol starting with a `.'. Only those present +in BODY are let-bound and this search is done at compile time. + +For instance, the following code + + (let-alist alist + (if (and .title .body) + .body + .site + .site.contents)) + +essentially expands to + + (let ((.title (cdr (assq 'title alist))) + (.body (cdr (assq 'body alist))) + (.site (cdr (assq 'site alist))) + (.site.contents (cdr (assq 'contents (cdr (assq 'site alist)))))) + (if (and .title .body) + .body + .site + .site.contents)) + +If you nest `let-alist' invocations, the inner one can't access +the variables of the outer one. You can, however, access alists +inside the original alist by using dots inside the symbol, as +displayed in the example above." + (declare (indent 1) (debug t)) + (let ((var (make-symbol "alist"))) + `(let ((,var ,alist)) + (let ,(mapcar (lambda (x) `(,(car x) ,(let-alist--access-sexp (car x) var))) + (delete-dups (let-alist--deep-dot-search body))) + ,@body)))) + +;;;; ChangeLog: + +;; 2015-06-11 Artur Malabarba <bruce.connor.am@gmail.com> +;; +;; * let-alist (let-alist--deep-dot-search): Fix cons +;; +;; 2015-03-07 Artur Malabarba <bruce.connor.am@gmail.com> +;; +;; let-alist: Update copyright +;; +;; 2014-12-22 Artur Malabarba <bruce.connor.am@gmail.com> +;; +;; packages/let-alist: Use `make-symbol' instead of `gensym'. +;; +;; 2014-12-20 Artur Malabarba <bruce.connor.am@gmail.com> +;; +;; packages/let-alist: Enable access to deeper alists +;; +;; 2014-12-14 Artur Malabarba <bruce.connor.am@gmail.com> +;; +;; let-alist.el: Add lexical binding. Version bump. +;; +;; 2014-12-11 Artur Malabarba <bruce.connor.am@gmail.com> +;; +;; let-alist: New package +;; + + +(provide 'let-alist) + +;;; let-alist.el ends here diff --git a/.emacs.d/initlibs/smex.el b/.emacs.d/initlibs/smex.el new file mode 100644 index 00000000..36d5b2e4 --- /dev/null +++ b/.emacs.d/initlibs/smex.el @@ -0,0 +1,446 @@ +;;; smex.el --- M-x interface with Ido-style fuzzy matching. + +;; Copyright (C) 2009-2014 Cornelius Mika and contributors +;; +;; Author: Cornelius Mika <cornelius.mika@gmail.com> and contributors +;; URL: http://github.com/nonsequitur/smex/ +;; Version: 3.0 +;; Keywords: convenience, usability + +;; This file is not part of GNU Emacs. + +;;; License: + +;; Licensed under the same terms as Emacs. + +;;; Commentary: + +;; Quick start: +;; run (smex-initialize) +;; +;; Bind the following commands: +;; smex, smex-major-mode-commands +;; +;; For a detailed introduction see: +;; http://github.com/nonsequitur/smex/blob/master/README.markdown + +;;; Code: + +(require 'ido) + +(defgroup smex nil + "M-x interface with Ido-style fuzzy matching and ranking heuristics." + :group 'extensions + :group 'convenience + :link '(emacs-library-link :tag "Lisp File" "smex.el")) + +(defcustom smex-auto-update t + "If non-nil, `Smex' checks for new commands each time it is run. +Turn it off for minor speed improvements on older systems." + :type 'boolean + :group 'smex) + +(defcustom smex-save-file "~/.smex-items" + "File in which the smex state is saved between Emacs sessions. +Variables stored are: `smex-data', `smex-history'. +Must be set before initializing Smex." + :type 'string + :group 'smex) + +(defcustom smex-history-length 7 + "Determines on how many recently executed commands +Smex should keep a record. +Must be set before initializing Smex." + :type 'integer + :group 'smex) + +(defcustom smex-prompt-string "M-x " + "String to display in the Smex prompt." + :type 'string + :group 'smex) + +(defcustom smex-flex-matching t + "Enables Ido flex matching. On by default. +Set this to nil to disable fuzzy matching." + :type 'boolean + :group 'smex) + +(defvar smex-initialized-p nil) +(defvar smex-cache) +(defvar smex-ido-cache) +(defvar smex-data) +(defvar smex-history) +(defvar smex-command-count 0) +(defvar smex-custom-action nil) + +;;-------------------------------------------------------------------------------- +;; Smex Interface + +;;;###autoload +(defun smex () + (interactive) + (unless smex-initialized-p + (smex-initialize)) + (if (smex-already-running) + (smex-update-and-rerun) + (and smex-auto-update + (smex-detect-new-commands) + (smex-update)) + (smex-read-and-run smex-ido-cache))) + +(defsubst smex-already-running () + (and (boundp 'ido-choice-list) (eql ido-choice-list smex-ido-cache))) + +(defsubst smex-update-and-rerun () + (smex-do-with-selected-item + (lambda (ignore) (smex-update) (smex-read-and-run smex-ido-cache ido-text)))) + +(defun smex-read-and-run (commands &optional initial-input) + (let* ((chosen-item-name (smex-completing-read commands initial-input)) + (chosen-item (intern chosen-item-name))) + (if smex-custom-action + (let ((action smex-custom-action)) + (setq smex-custom-action nil) + (funcall action chosen-item)) + (unwind-protect + (execute-extended-command current-prefix-arg chosen-item-name) + (smex-rank chosen-item))))) + +(defun smex-major-mode-commands () + "Like `smex', but limited to commands that are relevant to the active major mode." + (interactive) + (let ((commands (delete-dups (append (smex-extract-commands-from-keymap (current-local-map)) + (smex-extract-commands-from-features major-mode))))) + (setq commands (smex-sort-according-to-cache commands)) + (setq commands (mapcar #'symbol-name commands)) + (smex-read-and-run commands))) + +(defun smex-completing-read (choices initial-input) + (let ((ido-completion-map ido-completion-map) + (ido-setup-hook (cons 'smex-prepare-ido-bindings ido-setup-hook)) + (ido-enable-prefix nil) + (ido-enable-flex-matching smex-flex-matching) + (ido-max-prospects 10) + (minibuffer-completion-table choices)) + (ido-completing-read (smex-prompt-with-prefix-arg) choices nil nil + initial-input 'extended-command-history (car choices)))) + +(defun smex-prompt-with-prefix-arg () + (if (not current-prefix-arg) + smex-prompt-string + (concat + (if (eq current-prefix-arg '-) + "- " + (if (integerp current-prefix-arg) + (format "%d " current-prefix-arg) + (if (= (car current-prefix-arg) 4) + "C-u " + (format "%d " (car current-prefix-arg))))) + smex-prompt-string))) + +(defun smex-prepare-ido-bindings () + (define-key ido-completion-map (kbd "TAB") 'minibuffer-complete) + (define-key ido-completion-map (kbd "C-h f") 'smex-describe-function) + (define-key ido-completion-map (kbd "C-h w") 'smex-where-is) + (define-key ido-completion-map (kbd "M-.") 'smex-find-function) + (define-key ido-completion-map (kbd "C-a") 'move-beginning-of-line)) + +;;-------------------------------------------------------------------------------- +;; Cache and Maintenance + +(defun smex-rebuild-cache () + (interactive) + (setq smex-cache nil) + + ;; Build up list 'new-commands' and later put it at the end of 'smex-cache'. + ;; This speeds up sorting. + (let (new-commands) + (mapatoms (lambda (symbol) + (when (commandp symbol) + (let ((known-command (assq symbol smex-data))) + (if known-command + (setq smex-cache (cons known-command smex-cache)) + (setq new-commands (cons (list symbol) new-commands))))))) + (if (eq (length smex-cache) 0) + (setq smex-cache new-commands) + (setcdr (last smex-cache) new-commands))) + + (setq smex-cache (sort smex-cache 'smex-sorting-rules)) + (smex-restore-history) + (setq smex-ido-cache (smex-convert-for-ido smex-cache))) + +(defun smex-convert-for-ido (command-items) + (mapcar (lambda (command-item) (symbol-name (car command-item))) command-items)) + +(defun smex-restore-history () + "Rearranges `smex-cache' according to `smex-history'" + (if (> (length smex-history) smex-history-length) + (setcdr (nthcdr (- smex-history-length 1) smex-history) nil)) + (mapc (lambda (command) + (unless (eq command (caar smex-cache)) + (let ((command-cell-position (smex-detect-position smex-cache (lambda (cell) + (eq command (caar cell)))))) + (if command-cell-position + (let ((command-cell (smex-remove-nth-cell command-cell-position smex-cache))) + (setcdr command-cell smex-cache) + (setq smex-cache command-cell)))))) + (reverse smex-history))) + +(defun smex-sort-according-to-cache (list) + "Sorts a list of commands by their order in `smex-cache'" + (let (sorted) + (dolist (command-item smex-cache) + (let ((command (car command-item))) + (when (memq command list) + (setq sorted (cons command sorted)) + (setq list (delq command list))))) + (nreverse (append list sorted)))) + +(defun smex-update () + (interactive) + (smex-save-history) + (smex-rebuild-cache)) + +(defun smex-detect-new-commands () + (let ((i 0)) + (mapatoms (lambda (symbol) (if (commandp symbol) (setq i (1+ i))))) + (unless (= i smex-command-count) + (setq smex-command-count i)))) + +(defun smex-auto-update (&optional idle-time) + "Update Smex when Emacs has been idle for IDLE-TIME." + (unless idle-time (setq idle-time 60)) + (run-with-idle-timer idle-time t + '(lambda () (if (smex-detect-new-commands) (smex-update))))) + +;;;###autoload +(defun smex-initialize () + (interactive) + (unless ido-mode (smex-initialize-ido)) + (smex-load-save-file) + (smex-detect-new-commands) + (smex-rebuild-cache) + (add-hook 'kill-emacs-hook 'smex-save-to-file) + (setq smex-initialized-p t)) + +(defun smex-initialize-ido () + "Sets up a minimal Ido environment for `ido-completing-read'." + (ido-init-completion-maps) + (add-hook 'minibuffer-setup-hook 'ido-minibuffer-setup)) + +(defun smex-load-save-file () + "Loads `smex-history' and `smex-data' from `smex-save-file'" + (let ((save-file (expand-file-name smex-save-file))) + (if (file-readable-p save-file) + (with-temp-buffer + (insert-file-contents save-file) + (condition-case nil + (setq smex-history (read (current-buffer)) + smex-data (read (current-buffer))) + (error (if (smex-save-file-not-empty-p) + (error "Invalid data in smex-save-file (%s). Can't restore history." + smex-save-file) + (if (not (boundp 'smex-history)) (setq smex-history)) + (if (not (boundp 'smex-data)) (setq smex-data)))))) + (setq smex-history nil smex-data nil)))) + +(defsubst smex-save-file-not-empty-p () + (string-match-p "\[^[:space:]\]" (buffer-string))) + +(defun smex-save-history () + "Updates `smex-history'" + (setq smex-history nil) + (let ((cell smex-cache)) + (dotimes (i smex-history-length) + (setq smex-history (cons (caar cell) smex-history)) + (setq cell (cdr cell)))) + (setq smex-history (nreverse smex-history))) + +(defun smex-save-to-file () + (interactive) + (smex-save-history) + (with-temp-file (expand-file-name smex-save-file) + (ido-pp 'smex-history) + (ido-pp 'smex-data))) + +;;-------------------------------------------------------------------------------- +;; Ranking + +(defun smex-sorting-rules (command-item other-command-item) + "Returns true if COMMAND-ITEM should sort before OTHER-COMMAND-ITEM." + (let* ((count (or (cdr command-item ) 0)) + (other-count (or (cdr other-command-item) 0)) + (name (car command-item)) + (other-name (car other-command-item)) + (length (length (symbol-name name))) + (other-length (length (symbol-name other-name)))) + (or (> count other-count) ; 1. Frequency of use + (and (= count other-count) + (or (< length other-length) ; 2. Command length + (and (= length other-length) + (string< name other-name))))))) ; 3. Alphabetical order + +(defun smex-rank (command) + (let ((command-item (or (assq command smex-cache) + ;; Update caches and try again if not found. + (progn (smex-update) + (assq command smex-cache))))) + (when command-item + (smex-update-counter command-item) + + ;; Don't touch the cache order if the chosen command + ;; has just been execucted previously. + (unless (eq command-item (car smex-cache)) + (let (command-cell + (pos (smex-detect-position smex-cache (lambda (cell) + (eq command-item (car cell)))))) + ;; Remove the just executed command. + (setq command-cell (smex-remove-nth-cell pos smex-cache)) + ;; And put it on top of the cache. + (setcdr command-cell smex-cache) + (setq smex-cache command-cell) + + ;; Repeat the same for the ido cache. Should this be DRYed? + (setq command-cell (smex-remove-nth-cell pos smex-ido-cache)) + (setcdr command-cell smex-ido-cache) + (setq smex-ido-cache command-cell) + + ;; Now put the last history item back to its normal place. + (smex-sort-item-at smex-history-length)))))) + +(defun smex-update-counter (command-item) + (let ((count (cdr command-item))) + (setcdr command-item + (if count + (1+ count) + ;; Else: Command has just been executed for the first time. + ;; Add it to `smex-data'. + (if smex-data + (setcdr (last smex-data) (list command-item)) + (setq smex-data (list command-item))) + 1)))) + +(defun smex-sort-item-at (n) + "Sorts item at position N in `smex-cache'." + (let* ((command-cell (nthcdr n smex-cache)) + (command-item (car command-cell)) + (command-count (cdr command-item))) + (let ((insert-at (smex-detect-position command-cell (lambda (cell) + (smex-sorting-rules command-item (car cell)))))) + ;; TODO: Should we handle the case of 'insert-at' being nil? + ;; This will never happen in practice. + (when (> insert-at 1) + (setq command-cell (smex-remove-nth-cell n smex-cache)) + ;; smex-cache just got shorter by one element, so subtract '1' from insert-at. + (setq insert-at (+ n (- insert-at 1))) + (smex-insert-cell command-cell insert-at smex-cache) + + ;; Repeat the same for the ido cache. DRY? + (setq command-cell (smex-remove-nth-cell n smex-ido-cache)) + (smex-insert-cell command-cell insert-at smex-ido-cache))))) + +(defun smex-detect-position (cell function) + "Detects, relatively to CELL, the position of the cell +on which FUNCTION returns true. +Only checks cells after CELL, starting with the cell right after CELL. +Returns nil when reaching the end of the list." + (let ((pos 1)) + (catch 'break + (while t + (setq cell (cdr cell)) + (if (not cell) + (throw 'break nil) + (if (funcall function cell) (throw 'break pos)) + (setq pos (1+ pos))))))) + +(defun smex-remove-nth-cell (n list) + "Removes and returns the Nth cell in LIST." + (let* ((previous-cell (nthcdr (- n 1) list)) + (result (cdr previous-cell))) + (setcdr previous-cell (cdr result)) + result)) + +(defun smex-insert-cell (new-cell n list) + "Inserts cell at position N in LIST." + (let* ((cell (nthcdr (- n 1) list)) + (next-cell (cdr cell))) + (setcdr (setcdr cell new-cell) next-cell))) + +;;-------------------------------------------------------------------------------- +;; Help and Reference + +(defun smex-do-with-selected-item (fn) + (setq smex-custom-action fn) + (ido-exit-minibuffer)) + +(defun smex-describe-function () + (interactive) + (smex-do-with-selected-item (lambda (chosen) + (describe-function chosen) + (pop-to-buffer "*Help*")))) + +(defun smex-where-is () + (interactive) + (smex-do-with-selected-item 'where-is)) + +(defun smex-find-function () + (interactive) + (smex-do-with-selected-item 'find-function)) + +(defun smex-extract-commands-from-keymap (map) + (let (commands) + (smex-parse-keymap map commands) + commands)) + +(defun smex-parse-keymap (map commands) + (map-keymap (lambda (binding element) + (if (and (listp element) (eq 'keymap (car element))) + (smex-parse-keymap element commands) + ; Strings are commands, too. Reject them. + (if (and (symbolp element) (commandp element)) + (push element commands)))) + map)) + +(defun smex-extract-commands-from-features (mode) + (let ((library-path (symbol-file mode)) + (mode-name (symbol-name mode)) + commands) + + (string-match "\\(.+?\\)\\(-mode\\)?$" mode-name) + ;; 'lisp-mode' -> 'lisp' + (setq mode-name (match-string 1 mode-name)) + (if (string= mode-name "c") (setq mode-name "cc")) + (setq mode-name (regexp-quote mode-name)) + + (dolist (feature load-history) + (let ((feature-path (car feature))) + (when (and feature-path (or (equal feature-path library-path) + (string-match mode-name (file-name-nondirectory + feature-path)))) + (dolist (item (cdr feature)) + (if (and (listp item) (eq 'defun (car item))) + (let ((function (cdr item))) + (when (commandp function) + (setq commands (append commands (list function)))))))))) + commands)) + +(defun smex-show-unbound-commands () + "Shows unbound commands in a new buffer, +sorted by frequency of use." + (interactive) + (setq smex-data (sort smex-data 'smex-sorting-rules)) + (let ((unbound-commands (delq nil + (mapcar (lambda (command-item) + (unless (where-is-internal (car command-item)) + command-item)) + smex-data)))) + (view-buffer-other-window "*Smex: Unbound Commands*") + (setq buffer-read-only t) + (let ((inhibit-read-only t)) + (erase-buffer) + (ido-pp 'unbound-commands)) + (set-buffer-modified-p nil) + (goto-char (point-min)))) + +(provide 'smex) +;;; smex.el ends here diff --git a/.emacs.d/initlibs/use-package.el b/.emacs.d/initlibs/use-package.el new file mode 100644 index 00000000..4928ece0 --- /dev/null +++ b/.emacs.d/initlibs/use-package.el @@ -0,0 +1,1194 @@ +;;; use-package.el --- A use-package declaration for simplifying your .emacs + +;; Copyright (C) 2012 John Wiegley + +;; Author: John Wiegley <jwiegley@gmail.com> +;; Maintainer: John Wiegley <jwiegley@gmail.com> +;; Created: 17 Jun 2012 +;; Modified: 17 Oct 2016 +;; Version: 2.3 +;; Package-Requires: ((bind-key "1.0") (diminish "0.44")) +;; Keywords: dotemacs startup speed config package +;; 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 2, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; 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 'bind-key) +(require 'bytecomp) +(require 'diminish nil t) +(require 'bytecomp) +(eval-when-compile (require 'cl)) +(eval-when-compile (require 'regexp-opt)) + +(declare-function package-installed-p "package") + +(defgroup use-package nil + "A use-package declaration for simplifying your `.emacs'." + :group 'startup) + +(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" nil) (const :tag "Verbose" t) + (const :tag "Debug" debug)) + :group 'use-package) + +(defcustom use-package-debug nil + "Whether to display use-package expansions in a *use-package* buffer." + :type 'boolean + :group 'use-package) + +(defcustom use-package-check-before-init nil + "If non-nil, check that package exists before executing its `:init' block. +The check is performed by looking for the module using `locate-library'." + :type 'boolean + :group 'use-package) + +(defcustom use-package-always-defer nil + "If non-nil, assume `:defer t` unless `:demand t` is given." + :type 'boolean + :group 'use-package) + +(defcustom use-package-always-ensure nil + "Treat every package as though it had specified `:ensure SEXP`." + :type 'sexp + :group 'use-package) + +(defcustom use-package-always-pin nil + "Treat every package as though it had specified `:pin SYM." + :type 'symbol + :group 'use-package) + +(defcustom use-package-minimum-reported-time 0.1 + "Minimal load time that will be reported. + +Note that `use-package-verbose' has to be set to t, for anything +to be reported at all. + +If you customize this, then you should require the `use-package' +feature in files that use `use-package', even if these files only +contain compiled expansions of the macros. If you don't do so, +then the expanded macros do their job silently." + :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 evalaution of a +`use-package` declaration, and exercise some control over what +happens. + +Note that if either `pre-init' hooks returns a nil value, that +block's user-supplied configuration is not evaluated, so be +certain to return `t' if you only wish to add behavior to what +the user specified." + :type 'boolean + :group 'use-package) + +(defcustom use-package-keywords + '(:disabled + :pin + :ensure + :if + :when + :unless + :requires + :load-path + :preface + :no-require + :bind + :bind* + :bind-keymap + :bind-keymap* + :interpreter + :mode + :commands + :defines + :functions + :defer + :init + :after + :demand + :config + :diminish + :delight) + "Establish which keywords are valid, and the order they are processed in. + +Note that `:disabled' is special, in that it causes nothing at all to happen, +even if the rest of the use-package declaration is incorrect." + :type '(repeat symbol) + :group 'use-package) + +(defcustom use-package-expand-minimally nil + "If non-nil, make the expanded code as minimal as possible. +This disables: + - Printing to the *Messages* buffer of slowly-evaluating forms + - Capture of load errors (normally redisplayed as warnings) + - Conditional loading of packages (load failures become errors) +The only advantage is that, if you know your configuration works, +then your byte-compiled init file is as minimal as possible." + :type 'boolean + :group 'use-package) + +(defcustom use-package-enable-imenu-support nil + "If non-nil, adjust `lisp-imenu-generic-expression' to include +support for finding `use-package' and `require' forms. + +Must be set before loading use-package." + :type 'boolean + :group 'use-package) + +(when use-package-enable-imenu-support + ;; Not defined in Emacs 24 + (defvar lisp-mode-symbol-regexp + "\\(?:\\sw\\|\\s_\\|\\\\.\\)+") + (add-to-list + 'lisp-imenu-generic-expression + (list "Package" + (purecopy (concat "^\\s-*(" + (eval-when-compile + (regexp-opt + '("use-package" "require") + t)) + "\\s-+\\(" lisp-mode-symbol-regexp "\\)")) + 2))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Utility functions +;; + +(defun use-package-as-symbol (string-or-symbol) + "If STRING-OR-SYMBOL is already a symbol, return it. Otherwise +convert it to a symbol and return that." + (if (symbolp string-or-symbol) string-or-symbol + (intern string-or-symbol))) + +(defun use-package-as-string (string-or-symbol) + "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))) + +(defun use-package-load-name (name &optional noerror) + "Return a form which will load or require NAME depending on +whether it's a string or symbol." + (if (stringp name) + `(load ,name ',noerror) + `(require ',name nil ',noerror))) + +(defun use-package-expand (name label form) + "FORM is a list of forms, so `((foo))' if only `foo' is being called." + (declare (indent 1)) + (when form + (if use-package-expand-minimally + form + (let ((err (make-symbol "err"))) + (list + `(condition-case-unless-debug ,err + ,(macroexp-progn form) + (error + (ignore + (display-warning 'use-package + (format "%s %s: %s" + ,name ,label (error-message-string ,err)) + :error))))))))) + +(put 'use-package-expand 'lisp-indent-function 'defun) + +(defun use-package-hook-injector (name-string keyword body) + "Wrap pre/post hook injections around a given keyword form. +ARGS is a list of forms, so `((foo))' if only `foo' is being called." + (if (not use-package-inject-hooks) + (use-package-expand name-string (format "%s" keyword) body) + (let ((keyword-name (substring (format "%s" keyword) 1))) + (when body + `((when ,(macroexp-progn + (use-package-expand name-string (format "pre-%s hook" keyword) + `((run-hook-with-args-until-failure + ',(intern (concat "use-package--" name-string + "--pre-" keyword-name "-hook")))))) + ,(macroexp-progn + (use-package-expand name-string (format "%s" keyword) body)) + ,(macroexp-progn + (use-package-expand name-string (format "post-%s hook" keyword) + `((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) + +(defsubst use-package-error (msg) + "Report MSG as an error, so the user knows it came from this package." + (error "use-package: %s" msg)) + +(defsubst use-package-plist-maybe-put (plist property value) + "Add a VALUE for PROPERTY to PLIST, if it does not already exist." + (if (plist-member plist property) + plist + (plist-put plist property value))) + +(defsubst use-package-plist-cons (plist property value) + "Cons VALUE onto the head of the list at PROPERTY in PLIST." + (plist-put plist property (cons value (plist-get plist property)))) + +(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-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-split-list (pred xs) + (let ((ys (list nil)) (zs (list nil)) flip) + (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-keyword-index (keyword) + (loop named outer + with index = 0 + for k in use-package-keywords do + (if (eq k keyword) + (return-from outer index)) + (incf index))) + +(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) + (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))) + +(defsubst use-package-concat (&rest elems) + "Delete all empty lists from ELEMS (nil or (list nil)), and append them." + (apply #'nconc (delete nil (delete (list nil) elems)))) + +(defconst use-package-font-lock-keywords + '(("(\\(use-package\\)\\_>[ \t']*\\(\\(?:\\sw\\|\\s_\\)+\\)?" + (1 font-lock-keyword-face) + (2 font-lock-constant-face nil t)))) + +(font-lock-add-keywords 'emacs-lisp-mode use-package-font-lock-keywords) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Keyword processing +;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Normalization functions +;; + +(defun use-package-normalize-plist (name input) + "Given a pseudo-plist, normalize it to a regular plist." + (unless (null input) + (let* ((keyword (car input)) + (xs (use-package-split-list #'keywordp (cdr input))) + (args (car xs)) + (tail (cdr xs)) + (normalizer (intern (concat "use-package-normalize/" + (symbol-name keyword)))) + (arg + (cond + ((eq keyword :disabled) + (use-package-normalize-plist name tail)) + ((functionp normalizer) + (funcall normalizer name keyword args)) + ((= (length args) 1) + (car args)) + (t + args)))) + (if (memq keyword use-package-keywords) + (cons keyword + (cons arg (use-package-normalize-plist name tail))) + (use-package-error (format "Unrecognized keyword: %s" keyword)))))) + +(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) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :pin +;; + +(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-normalize/:pin (name 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)")))))) + +(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) +(defun use-package-normalize/:ensure (name keyword args) + (if (null args) + t + (use-package-only-one (symbol-name keyword) args + (lambda (label arg) + (if (symbolp arg) + arg + (use-package-error + (concat ":ensure wants an optional package name " + "(an unquoted symbol name)"))))))) + +(defun use-package-ensure-elpa (package &optional no-refresh) + (if (package-installed-p package) + t + (if (and (not no-refresh) + (assoc package (bound-and-true-p package-pinned-packages))) + (package-read-all-archive-contents)) + (if (or (assoc package package-archive-contents) no-refresh) + (package-install package) + (progn + (package-refresh-contents) + (use-package-ensure-elpa package t))))) + +(defun use-package-handler/:ensure (name keyword ensure rest state) + (let* ((body (use-package-process-keywords name rest state)) + (package-name (or (and (eq ensure t) (use-package-as-symbol name)) ensure)) + (ensure-form (if package-name + `(progn (require 'package) + (use-package-ensure-elpa ',package-name))))) + ;; We want to avoid installing packages when the `use-package' + ;; macro is being macro-expanded by elisp completion (see + ;; `lisp--local-variables'), but still do install packages when + ;; byte-compiling to avoid requiring `package' at runtime. + (if (bound-and-true-p byte-compile-current-file) + (eval ensure-form) ; Eval when byte-compiling, + (push ensure-form body)) ; or else wait until runtime. + body)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :if, :when and :unless +;; + +(defsubst use-package-normalize-value (label arg) + "Normalize a value." + (cond ((symbolp arg) + `(symbol-value ',arg)) + ((functionp arg) + `(funcall #',arg)) + (t arg))) + +(defun use-package-normalize-test (name keyword args) + (use-package-only-one (symbol-name keyword) args + #'use-package-normalize-value)) + +(defalias 'use-package-normalize/:if 'use-package-normalize-test) +(defalias 'use-package-normalize/:when 'use-package-normalize-test) +(defalias 'use-package-normalize/:unless 'use-package-normalize-test) + +(defun use-package-handler/:if (name keyword pred rest state) + (let ((body (use-package-process-keywords name rest state))) + `((when ,pred ,@body)))) + +(defalias 'use-package-handler/:when 'use-package-handler/:if) + +(defun use-package-handler/:unless (name keyword pred rest state) + (let ((body (use-package-process-keywords name rest state))) + `((unless ,pred ,@body)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :requires +;; + +(defun use-package-as-one (label args f) + "Call F on the first element of ARGS if it has one element, or all of ARGS." + (declare (indent 1)) + (if (and (listp args) (listp (cdr args))) + (if (= (length args) 1) + (funcall f label (car args)) + (funcall f label args)) + (use-package-error + (concat label " wants a list")))) + +(put 'use-package-as-one 'lisp-indent-function 'defun) + +(defun use-package-normalize-symbols (label arg &optional recursed) + "Normalize a list of symbols." + (cond + ((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)) + +(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 (listp requires) + `(not (member nil (mapcar #'featurep ',requires))) + `(featurep ',requires)) + ,@body))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :load-path +;; + +(defun use-package-normalize-paths (label arg &optional recursed) + "Normalize a list of filesystem paths." + (cond + ((and arg (or (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/: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 +;; + +(defun use-package-normalize-predicate (name keyword args) + (if (null args) + t + (use-package-only-one (symbol-name keyword) args + #'use-package-normalize-value))) + +(defalias 'use-package-normalize/:no-require 'use-package-normalize-predicate) + +(defun use-package-handler/:no-require (name keyword arg rest state) + ;; This keyword has no functional meaning. + (use-package-process-keywords name rest state)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :preface +;; + +(defun use-package-normalize-form (label args) + "Given a list of forms, return it wrapped in `progn'." + (unless (listp (car args)) + (use-package-error (concat label " wants a sexp or list of sexps"))) + (mapcar #'(lambda (form) + (if (and (consp form) + (eq (car form) 'use-package)) + (macroexpand form) + form)) args)) + +(defun use-package-normalize-forms (name keyword args) + (use-package-normalize-form (symbol-name keyword) args)) + +(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 + (unless (null arg) + `((eval-and-compile ,@arg))) + body))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :bind, :bind* +;; + +(defsubst use-package-is-sympair (x &optional allow-vector) + "Return t if X has the type (STRING . SYMBOL)." + (and (consp x) + (or (stringp (car x)) + (and allow-vector (vectorp (car x)))) + (symbolp (cdr x)))) + +(defsubst use-package-is-string-pair (x) + "Return t if X has the type (STRING . STRING)." + (and (consp x) + (stringp (car x)) + (stringp (cdr x)))) + +(defun use-package-normalize-pairs + (name label arg &optional recursed allow-vector allow-string-cdrs) + "Normalize a list of string/symbol pairs. +If RECURSED is non-nil, recurse into sublists. +If ALLOW-VECTOR is non-nil, then the key to bind may specify a +vector of keys, as accepted by `define-key'. +If ALLOW-STRING-CDRS is non-nil, then the command name to bind to +may also be a string, as accepted by `define-key'." + (cond + ((or (stringp arg) (and allow-vector (vectorp arg))) + (list (cons arg (use-package-as-symbol name)))) + ((use-package-is-sympair arg allow-vector) + (list arg)) + ((and (not recursed) (listp arg) (listp (cdr arg))) + (mapcar #'(lambda (x) + (let ((ret (use-package-normalize-pairs + name label x t allow-vector allow-string-cdrs))) + (if (listp ret) + (car ret) + ret))) arg)) + ((and allow-string-cdrs (use-package-is-string-pair arg)) + (list arg)) + (t arg))) + +(defun use-package-normalize-binder (name keyword args) + (use-package-as-one (symbol-name keyword) args + (lambda (label arg) + (use-package-normalize-pairs name label arg nil t t)))) + +(defalias 'use-package-normalize/:bind 'use-package-normalize-binder) +(defalias 'use-package-normalize/:bind* 'use-package-normalize-binder) + +(defun use-package-handler/:bind + (name keyword arg rest state &optional bind-macro) + (let ((commands (remq nil (mapcar #'(lambda (arg) + (if (listp arg) + (cdr arg) + nil)) arg)))) + (use-package-concat + (use-package-process-keywords name + (use-package-sort-keywords + (use-package-plist-maybe-put rest :defer t)) + (use-package-plist-append state :commands commands)) + `((ignore + ,(macroexpand + `(,(if bind-macro bind-macro 'bind-keys) + :package ,name ,@arg))))))) + +(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* +;; + +(defalias 'use-package-normalize/:bind-keymap 'use-package-normalize-binder) +(defalias 'use-package-normalize/:bind-keymap* 'use-package-normalize-binder) + +(defun use-package-autoload-keymap (keymap-symbol package override) + "Loads PACKAGE and then binds the key sequence used to invoke +this function to KEYMAP-SYMBOL. It then simulates pressing the +same key sequence a again, so that the next key pressed is routed +to the newly loaded keymap. + +This function supports use-package's :bind-keymap keyword. It +works by binding the given key sequence to an invocation of this +function for a particular keymap. The keymap is expected to be +defined by the package. In this way, loading the package is +deferred until the prefix key sequence is pressed." + (if (not (require package nil t)) + (use-package-error (format "Cannot load package.el: %s" package)) + (if (and (boundp keymap-symbol) + (keymapp (symbol-value keymap-symbol))) + (let* ((kv (this-command-keys-vector)) + (key (key-description kv)) + (keymap (symbol-value keymap-symbol))) + (if override + (bind-key* key keymap) + (bind-key key keymap)) + (setq unread-command-events + (listify-key-sequence kv))) + (use-package-error + (format "use-package: package.el %s failed to define keymap %s" + package keymap-symbol))))) + +(defun use-package-handler/:bind-keymap + (name keyword arg rest state &optional override) + (let ((form (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)))) arg))) + (use-package-concat + (use-package-process-keywords name + (use-package-sort-keywords + (use-package-plist-maybe-put rest :defer t)) + state) + `((ignore ,@form))))) + +(defun use-package-handler/:bind-keymap* (name keyword arg rest state) + (use-package-handler/:bind-keymap name keyword arg rest state t)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :interpreter +;; + +(defun use-package-normalize-mode (name keyword args) + (use-package-as-one (symbol-name keyword) args + (apply-partially #'use-package-normalize-pairs name))) + +(defalias 'use-package-normalize/:interpreter 'use-package-normalize-mode) + +(defun use-package-handler/:interpreter (name keyword arg rest state) + (let* (commands + (form (mapcar #'(lambda (interpreter) + (push (cdr interpreter) commands) + `(add-to-list 'interpreter-mode-alist ',interpreter)) arg))) + (use-package-concat + (use-package-process-keywords name + (use-package-sort-keywords + (use-package-plist-maybe-put rest :defer t)) + (use-package-plist-append state :commands commands)) + `((ignore ,@form))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :mode +;; + +(defalias 'use-package-normalize/:mode 'use-package-normalize-mode) + +(defun use-package-handler/:mode (name keyword arg rest state) + (let* (commands + (form (mapcar #'(lambda (mode) + (push (cdr mode) commands) + `(add-to-list 'auto-mode-alist ',mode)) arg))) + (use-package-concat + (use-package-process-keywords name + (use-package-sort-keywords + (use-package-plist-maybe-put rest :defer t)) + (use-package-plist-append state :commands commands)) + `((ignore ,@form))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :commands +;; + +(defalias 'use-package-normalize/:commands 'use-package-normalize-symlist) + +(defun use-package-handler/:commands (name keyword arg rest state) + ;; The actual processing for commands is done in :defer + (use-package-process-keywords name + (use-package-sort-keywords + (use-package-plist-maybe-put rest :defer t)) + (use-package-plist-append state :commands arg))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :defines +;; + +(defalias 'use-package-normalize/:defines 'use-package-normalize-symlist) + +(defun use-package-handler/:defines (name keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + body)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :functions +;; + +(defalias 'use-package-normalize/:functions 'use-package-normalize-symlist) + +(defun use-package-handler/:functions (name keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (if (not (bound-and-true-p byte-compile-current-file)) + body + (use-package-concat + (unless (null arg) + `((eval-when-compile + ,@(mapcar + #'(lambda (fn) + `(declare-function ,fn ,(use-package-as-string name))) arg)))) + body)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :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 + (plist-put state :deferred t))) + (name-string (use-package-as-string name))) + (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))) + + ;; Since we deferring load, establish any necessary autoloads, and also + ;; keep the byte-compiler happy. + (apply + #'nconc + (mapcar #'(lambda (command) + (when (not (stringp command)) + (append + `((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 (plist-get state :commands)))) + + body))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :after +;; + +(defalias 'use-package-normalize/:after 'use-package-normalize-symlist) + +(defun use-package-require-after-load (features name) + "Return form for after any of FEATURES require NAME." + `(progn + ,@(mapcar + (lambda (feat) + `(eval-after-load + (quote ,feat) + (quote (require (quote ,name) nil t)))) + features))) + +(defun use-package-handler/:after (name keyword arg rest state) + (let ((body (use-package-process-keywords name rest + (plist-put state :deferred t))) + (name-string (use-package-as-string name))) + (use-package-concat + (when arg + (list (use-package-require-after-load arg name))) + 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 + (use-package-plist-delete state :deferred))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :init +;; + +(defalias 'use-package-normalize/:init 'use-package-normalize-forms) + +(defun use-package-handler/:init (name keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + ;; The user's initializations + (let ((init-body + (use-package-hook-injector (use-package-as-string name) + :init arg))) + (if use-package-check-before-init + `((if (locate-library ,(use-package-as-string name)) + ,(macroexp-progn init-body))) + init-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)) + (config-body + (if (equal arg '(t)) + body + (use-package--with-elapsed-timer + (format "Configuring package %s" name-symbol) + (use-package-concat + (use-package-hook-injector (symbol-name name-symbol) + :config arg) + body + (list t)))))) + (if (plist-get state :deferred) + (unless (or (null config-body) (equal config-body '(t))) + `((eval-after-load ,(if (symbolp name) `',name name) + ',(macroexp-progn config-body)))) + (use-package--with-elapsed-timer + (format "Loading package %s" name) + (if use-package-expand-minimally + (use-package-concat + (list (use-package-load-name name)) + config-body) + `((if (not ,(use-package-load-name name t)) + (ignore + (message (format "Cannot load %s" ',name))) + ,@config-body))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :diminish + +(defun use-package-normalize-diminish (name label arg &optional recursed) + "Normalize the arguments to diminish down to a list of one of two forms: + SYMBOL + (SYMBOL . STRING)" + (cond + ((symbolp arg) + (list arg)) + ((stringp arg) + (list (cons (intern (concat (use-package-as-string name) "-mode")) 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"))))) + +(defun use-package-normalize/:diminish (name keyword args) + (use-package-as-one (symbol-name keyword) args + (apply-partially #'use-package-normalize-diminish name))) + +(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))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; :delight +;; + +(defun use-package-normalize/:delight (name keyword args) + "Normalize arguments to delight." + (cond + ((and (= (length args) 1) + (symbolp (car args))) + (list (car args) nil name)) + ((and (= (length args) 2) + (symbolp (car args))) + (list (car args) (cadr args) (use-package-as-symbol name))) + ((and (= (length args) 3) + (symbolp (car args))) + args) + (t + (use-package-error ":delight expects same args as delight function")))) + +(defun use-package-handler/:delight (name keyword args rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + body + `((delight (quote ,(nth 0 args)) ,(nth 1 args) (quote ,(nth 2 args))) t)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; The main macro +;; + +;;;###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'. +: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. + +: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' 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 Defer loading of a package until after any of the named + features are loaded. + +:demand Prevent deferred loading in all cases. + +: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). +:ensure Loads the package using package.el if necessary. +:pin Pin the package to an archive." + (declare (indent 1)) + (unless (member :disabled args) + (let* ((name-symbol (if (stringp name) (intern name) name)) + (args0 (use-package-plist-maybe-put + (use-package-normalize-plist name args) + :config '(t))) + (args* (use-package-sort-keywords + (if use-package-always-ensure + (use-package-plist-maybe-put + args0 :ensure use-package-always-ensure) + args0))) + (args* (use-package-sort-keywords + (if use-package-always-pin + (use-package-plist-maybe-put + args* :pin use-package-always-pin) + args*)))) + + ;; When byte-compiling, pre-load the package so all its symbols are in + ;; scope. + (if (bound-and-true-p byte-compile-current-file) + (setq args* + (use-package-plist-cons + args* :preface + `(eval-when-compile + ,@(mapcar #'(lambda (var) `(defvar ,var)) + (plist-get args* :defines)) + (with-demoted-errors + ,(format "Cannot load %s: %%S" name) + ,(if (eq use-package-verbose 'debug) + `(message "Compiling package %s" ',name-symbol)) + ,(unless (plist-get args* :no-require) + (use-package-load-name name))))))) + + (let ((body + (macroexp-progn + (use-package-process-keywords name args* + (and use-package-always-defer (list :deferred t)))))) + (if use-package-debug + (display-buffer + (save-current-buffer + (let ((buf (get-buffer-create "*use-package*"))) + (with-current-buffer buf + (delete-region (point-min) (point-max)) + (emacs-lisp-mode) + (insert (pp-to-string body))) + buf)))) + body)))) + + +(put 'use-package 'lisp-indent-function 'defun) + +(provide 'use-package) + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: + +;;; use-package.el ends here |