From 31ea42e15eb66dd238936e057ef1c6787d8d429e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 17 Jun 2012 04:40:25 -0500 Subject: [PATCH] README and other updates --- lisp/use-package/bind-key.el | 218 +++++++++++++++++++++ lisp/use-package/use-package.el | 322 ++++++++++++++++++++++++++++++++ 2 files changed, 540 insertions(+) create mode 100644 lisp/use-package/bind-key.el create mode 100644 lisp/use-package/use-package.el diff --git a/lisp/use-package/bind-key.el b/lisp/use-package/bind-key.el new file mode 100644 index 00000000000..75e3b12d9f7 --- /dev/null +++ b/lisp/use-package/bind-key.el @@ -0,0 +1,218 @@ +;;; bind-key --- A simple way to manage personal keybindings + +;; Copyright (C) 2012 John Wiegley + +;; Author: John Wiegley +;; Created: 16 Jun 2012 +;; Version: 1.0 +;; Keywords: keys keybinding config dotemacs +;; X-URL: https://github.com/jwiegley/bind-key + +;; 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 "" 'other-window) +;; +;; If you want to rebind a key only in a particular key, 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) +;; +;; 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 'easy-mmode) + +(defgroup bind-key nil + "A simple way to manage personal keybindings" + :group 'emacs) + +(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) + +;; 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 "" override-global-map) + +(add-hook 'after-init-hook + (function + (lambda () + (override-global-mode 1)))) + +(defvar personal-keybindings nil) + +(defmacro bind-key (key-name command &optional keymap) + (let ((namevar (make-symbol "name")) + (keyvar (make-symbol "key")) + (bindingvar (make-symbol "binding")) + (entryvar (make-symbol "entry"))) + `(let* ((,namevar ,(eval key-name)) + (,keyvar (read-kbd-macro ,namevar)) + (,bindingvar (lookup-key (or ,keymap global-map) + ,keyvar))) + (let ((,entryvar (assoc (cons ,namevar (quote ,keymap)) + personal-keybindings))) + (if ,entryvar + (setq personal-keybindings + (delq ,entryvar personal-keybindings)))) + (setq personal-keybindings + (cons (list (cons ,namevar (quote ,keymap)) + ,command + (unless (numberp ,bindingvar) ,bindingvar)) + personal-keybindings)) + (define-key (or ,keymap global-map) ,keyvar ,command)))) + +(defmacro unbind-key (key-name &optional keymap) + `(bind-key ,key-name nil ,keymap)) + +(defmacro bind-key* (key-name command) + `(progn + (bind-key ,key-name ,command) + (define-key override-global-map ,(read-kbd-macro key-name) ,command))) + +(defun get-binding-description (elem) + (cond + ((listp elem) + (cond + ((eq 'lambda (car elem)) + "#") + ((eq 'closure (car elem)) + "#") + ((eq 'keymap (car elem)) + "#") + (t + elem))) + ((keymapp elem) + "#") + ((symbolp elem) + elem) + (t + "#"))) + +(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))))) + +(defun describe-personal-keybindings () + (interactive) + (with-current-buffer (get-buffer-create "*Personal Keybindings*") + (delete-region (point-min) (point-max)) + (insert "Key name Command Comments +----------------- --------------------------------------- --------------------- +") + (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))) + (insert ?\n (format "\n%s\n%s\n\n" + (cdar binding) + (make-string 79 ?-))) + (if (and last-binding + (cdr (compare-keybindings last-binding binding))) + (insert ?\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)) + ) + (insert + (format + "%-18s%-40s%s\n" + key-name command-desc + (if (string= command-desc at-present-desc) + (if (or (null was-command) + (string= command-desc was-command-desc)) + "" + (format "(%s)" was-command-desc)) + (format "[now: %s]" at-present))))) + + (setq last-binding binding))) + + (goto-char (point-min)) + (display-buffer (current-buffer)))) + +(provide 'bind-key) + +;;; bind-key.el ends here diff --git a/lisp/use-package/use-package.el b/lisp/use-package/use-package.el new file mode 100644 index 00000000000..5bd8df7f9e4 --- /dev/null +++ b/lisp/use-package/use-package.el @@ -0,0 +1,322 @@ +;;; use-package --- A use-package declaration for simplifying your .emacs + +;; Copyright (C) 2012 John Wiegley + +;; Author: John Wiegley +;; Created: 17 Jun 2012 +;; Version: 1.0 +;; Keywords: dotemacs startup speed config package +;; X-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! +;; +;; Here is the simplest `use-package' declaration: +;; +;; (use-package foo) +;; +;; This loads in the package foo, but only if foo is available on your system. +;; If not, a warning is logged to your `*Messages*' buffer. If it succeeds a +;; message about "Loading foo" is logged, along with the time it took to load, +;; if that time is over 0.01s. +;; +;; Use the :init keywoard to do some stuff to initialize foo, but only if foo +;; actually gets loaded: +;; +;; (use-package foo +;; :init +;; (progn +;; (setq foo-variable t) +;; (foo-mode 1))) +;; +;; A very command thing to do when loading a module is to bind a key to +;; primary commands within that module: +;; +;; (use-package ace-jump-mode +;; :bind ("C-." . ace-jump-mode)) +;; +;; This does two things: first, it creates autoload for the `ace-jump-mode' +;; command, and defers loading of `ace-jump-mode' until you actually use it. +;; Second, it binds the key `C-.' to that command. After loading, you can use +;; `M-x describe-personal-keybindings' to see all such bindings you've set +;; throughout your Emacs. +;; +;; A more literal way to do the exact same thing is: +;; +;; (use-package ace-jump-mode +;; :commands ace-jump-mode +;; :init +;; (bind-key "C-." 'ace-jump-mode)) +;; +;; When you use the `:commands' keyword, it creates autoloads for those +;; commands and defers loading of the module until they are used. In this +;; case, the `:init' form is always run -- even if ace-jump-mode might not be +;; on your system. So remember to keep `:init' activities to only those that +;; would succeed either way. +;; +;; If you aren't used `:commands' or `:bind' (which implies `:commands'), you +;; can still defer loading with `:defer' keyword: +;; +;; (use-package ace-jump-mode +;; :defer t +;; :init +;; (progn +;; (autoload 'ace-jump-mode "ace-jump-mode" nil t) +;; (bind-key "C-." 'ace-jump-mode))) +;; +;; This does exactly the same thing as the other two commands above. +;; +;; A companion to the `:init' keyword is `:config'. Although `:init' always +;; happens in the case of deferred modules (which are likely to be the most +;; common kind), `:config' form only run after the module has been loaded by +;; Emacs: +;; +;; (use-package ace-jump-mode +;; :bind ("C-." . ace-jump-mode) +;; :config +;; (message "Yay, ace-jump-mode was actually loaded!")) +;; +;; You will see a "Configured..." message in your `*Messages*' log when a +;; package is configured, and a timing if the configuration time was longer +;; than 0.01s. You should keep `:init' forms as simple as possible, and put +;; as much as you can get away with on the `:config' side. +;; +;; You can have both `:init' and `:config': +;; +;; (use-package haskell-mode +;; :commands haskell-mode +;; :init +;; (add-to-list 'auto-mode-alist '("\\.l?hs$" . haskell-mode)) +;; :config +;; (progn +;; (use-package inf-haskell) +;; (use-package hs-lint))) +;; +;; In this case, I want to autoload the command `haskell-mode' from +;; "haskell-mode.el", add it to `auto-mode-alist' at the time ".emacs" is +;; loaded, but wait until after I've opened a Haskell file before loading +;; "inf-haskell.el" and "hs-lint.el". +;; +;; The `:bind' keyword takes either a cons or a list of conses: +;; +;; (use-package hi-lock +;; :bind (("M-o l" . highlight-lines-matching-regexp) +;; ("M-o r" . highlight-regexp) +;; ("M-o w" . highlight-phrase))) +;; +;; The `:commands' keyword likewise takes either a symbol or a list of +;; symbols. +;; +;; You can use the `:if' keyword to predicate the loading and initialization +;; of a module. For example, I only want an `edit-server' running for my +;; main, graphical Emacs, not for Emacsen I may start at the command line: +;; +;; (use-package edit-server +;; :if window-system +;; :init +;; (progn +;; (add-hook 'after-init-hook 'server-start t) +;; (add-hook 'after-init-hook 'edit-server-start t))) +;; +;; The `:disabled' keyword can be used to turn off a module that you're having +;; difficulties with, or to stop loading something you're not really using at +;; the present time: +;; +;; (use-package ess-site +;; :disabled t +;; :commands R) +;; +;; Another feature of `use-package' is that it always loads every file that it +;; can when your ".emacs" is being byte-compiled (if you do that, which I +;; recommend). This helps to silence spurious warnings about unknown +;; variables and functions. +;; +;; However, there are times when this is just not enough. For those times, +;; use the `:defines' keyword to introduce empty variable definitions solely +;; for the sake of the byte-compiler: +;; +;; (use-package texinfo +;; :defines texinfo-section-list +;; :commands texinfo-mode +;; :init +;; (add-to-list 'auto-mode-alist '("\\.texi$" . texinfo-mode))) +;; +;; If you need to silence a missing function warning, do it with an autoload +;; stub in your `:init' block: +;; +;; (use-package w3m +;; :commands (w3m-browse-url w3m-session-crash-recovery-remove) +;; :init +;; (eval-when-compile +;; (autoload 'w3m-search-escape-query-string "w3m-search"))) +;; +;; Lastly, `use-package' provides built-in support for the diminish utility, +;; if you have that installed. It's purpose is to remove strings from your +;; mode-line that would otherwise always be there and provide no useful +;; information. It is invoked with the `:diminish' keyword, which is passed +;; the minor mode symbol: +;; +;; (use-package abbrev +;; :diminish abbrev-mode +;; :init +;; (if (file-exists-p abbrev-file-name) +;; (quietly-read-abbrev-file)) +;; +;; :config +;; (add-hook 'expand-load-hook +;; (lambda () +;; (add-hook 'expand-expand-hook 'indent-according-to-mode) +;; (add-hook 'expand-jump-hook 'indent-according-to-mode)))) +;; +;; If you noticed that this declaration has neither a `:bind', `:commands' or +;; `:defer' keyword: congratulations, you're an A student! What it means is +;; that both the `:init' and `:config' forms will be executed when ".emacs" is +;; loaded, with no delays until later. Is this useful? Not really. I just +;; happen to like separating my configuration into things that must happen at +;; startup time, and things that could potentioally wait until after the +;; actual load. In this case, everything could be put inside `:init' and +;; there would be no difference. + +(require 'bind-key) + +(defgroup use-package nil + "A use-package declaration for simplifying your .emacs" + :group 'startup) + +;;;_ , Create use-package macro, to simplify customizations + +(eval-when-compile + (require 'cl)) + +(require 'bind-key) +(require 'diminish nil t) + +(defvar use-package-verbose t) + +(defmacro with-elapsed-timer (text &rest forms) + `(let ((now ,(if use-package-verbose + '(current-time)))) + ,(if use-package-verbose + `(message "%s..." ,text)) + ,@forms + ,(when use-package-verbose + `(let ((elapsed + (float-time (time-subtract (current-time) now)))) + (if (> elapsed 0.01) + (message "%s...done (%.3fs)" ,text elapsed) + (message "%s...done" ,text)))))) + +(put 'with-elapsed-timer 'lisp-indent-function 1) + +(defmacro use-package (name &rest args) + (let* ((commands (plist-get args :commands)) + (init-body (plist-get args :init)) + (config-body (plist-get args :config)) + (diminish-var (plist-get args :diminish)) + (defines (plist-get args :defines)) + (keybindings (plist-get args :bind)) + (predicate (plist-get args :if)) + (defines-eval (if (null defines) + nil + (if (listp defines) + (mapcar (lambda (var) `(defvar ,var)) defines) + `((defvar ,defines))))) + (requires (plist-get args :requires)) + (requires-test (if (null requires) + t + (if (listp requires) + `(not (member nil (mapcar #'featurep + (quote ,requires)))) + `(featurep (quote ,requires))))) + (name-string (if (stringp name) name + (symbol-name name)))) + + (if diminish-var + (setq config-body + `(progn + ,config-body + (ignore-errors + ,@(if (listp diminish-var) + (mapcar (lambda (var) `(diminish (quote ,var))) + diminish-var) + `((diminish (quote ,diminish-var)))))))) + + (when keybindings + (if (and commands (symbolp commands)) + (setq commands (list commands))) + (setq init-body + `(progn + ,init-body + ,@(mapcar #'(lambda (binding) + (push (cdr binding) commands) + `(bind-key ,(car binding) + (quote ,(cdr binding)))) + (if (and (consp keybindings) + (stringp (car keybindings))) + (list keybindings) + keybindings))))) + + (unless (plist-get args :disabled) + `(progn + (eval-when-compile + ,@defines-eval + ,(if (stringp name) + `(load ,name t) + `(require ',name nil t))) + ,(if (or commands (plist-get args :defer)) + (let (form) + (unless (listp commands) + (setq commands (list commands))) + (mapc #'(lambda (command) + (push `(autoload (function ,command) + ,name-string nil t) form)) + commands) + + `(when ,(or predicate t) + ,@form + ,init-body + ,(unless (null config-body) + `(eval-after-load ,name-string + '(if ,requires-test + (with-elapsed-timer + ,(format "Configuring package %s" name-string) + ,config-body)))) + t)) + `(if (and ,(or predicate t) + ,requires-test) + (if ,(if (stringp name) + `(load ,name t) + `(require ',name nil t)) + (with-elapsed-timer + ,(format "Loading package %s" name-string) + ,init-body + ,config-body + t) + (message "Could not load package %s" ,name-string)))))))) + +(put 'use-package 'lisp-indent-function 1) + +(provide 'use-package) + +;;; use-package.el ends here -- 2.39.2