al-key.el 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. ;;; al-key.el --- Additional functionality for working with key bindings
  2. ;; Copyright © 2013–2016, 2018 Alex Kost
  3. ;; This program is free software; you can redistribute it and/or modify
  4. ;; it under the terms of the GNU General Public License as published by
  5. ;; the Free Software Foundation, either version 3 of the License, or
  6. ;; (at your option) any later version.
  7. ;;
  8. ;; This program is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ;; GNU General Public License for more details.
  12. ;;
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Code:
  16. (require 'al-misc)
  17. ;;; Binding keys
  18. ;; `al/bind-keys' macro and related code originates from `bind-key'
  19. ;; package: <https://github.com/jwiegley/use-package>.
  20. (defvar al/override-global-map (make-keymap)
  21. "Keymap with key bindings to take precedence over other keymaps.")
  22. (define-minor-mode al/override-global-mode
  23. "Minor mode with key bindings to override other modes."
  24. t "")
  25. (add-to-list 'emulation-mode-map-alists
  26. `((al/override-global-mode . ,al/override-global-map)))
  27. (defun al/key-command (cmd-spec)
  28. "Return command value for `al/bind-key' macro."
  29. (cond ((null cmd-spec) nil)
  30. ((listp cmd-spec)
  31. (if (eq (car cmd-spec) 'lambda)
  32. cmd-spec
  33. `(lambda () (interactive) ,@cmd-spec)))
  34. (t `',cmd-spec)))
  35. (defmacro al/bind-key (key-name command &optional keymap)
  36. "Bind KEY-NAME to COMMAND in KEYMAP.
  37. KEY-NAME should be a string or a vector taken by `define-key'.
  38. COMMAND may be either:
  39. - nil (to unbind the key if it is already bound in KEYMAP),
  40. - a command name (an unquoted symbol),
  41. - or a list (it will be wrapped into interactive `lambda' form).
  42. If KEYMAP is not specified, use `global-map'.
  43. Examples:
  44. (al/bind-key \"C-f\" nil)
  45. (al/bind-key \"C-j\" newline lisp-mode-map)
  46. (al/bind-key [return] newline-and-indent lisp-mode-shared-map)
  47. (al/bind-key \"C-s-b\" ((backward-word) (backward-char)))"
  48. (let ((command (al/key-command command))
  49. (key-var (make-symbol "key"))
  50. (map-var (make-symbol "map")))
  51. `(let* ((,key-var ,key-name)
  52. (,key-var (if (stringp ,key-var)
  53. (read-kbd-macro ,key-var)
  54. ,key-var))
  55. (,map-var (or ,keymap global-map)))
  56. ,(if command
  57. `(define-key ,map-var ,key-var ,command)
  58. `(when (lookup-key ,map-var ,key-var)
  59. (define-key ,map-var ,key-var nil))))))
  60. (defmacro al/bind-key* (key-name command)
  61. `(al/bind-key ,key-name ,command al/override-global-map))
  62. (defmacro al/bind-keys (&rest args)
  63. "Bind multiple keys.
  64. ARGS are keyword arguments and key specifications. The following
  65. optional keywords are available:
  66. - `:map' - a keymap into which the key bindings should be added.
  67. - `:prefix-map' - name of a prefix map that should be created
  68. for these bindings.
  69. - `:prefix' - prefix key for these bindings.
  70. - `:prefix-docstring' - docstring of the prefix map variable.
  71. The rest ARGS are conses of key binding strings and functions.
  72. See `al/bind-key' for details."
  73. (let* ((map (plist-get args :map))
  74. (doc (plist-get args :prefix-docstring))
  75. (prefix-map (plist-get args :prefix-map))
  76. (prefix (plist-get args :prefix))
  77. (bindings (progn
  78. (while (keywordp (car args))
  79. (pop args)
  80. (pop args))
  81. args)))
  82. (or (and prefix prefix-map)
  83. (and (not prefix) (not prefix-map))
  84. (error "Both :prefix-map and :prefix must be supplied"))
  85. `(progn
  86. ,(when prefix-map
  87. `(progn
  88. (defvar ,prefix-map)
  89. ,(when doc
  90. `(put ',prefix-map 'variable-documentation ,doc))
  91. (define-prefix-command ',prefix-map)
  92. (al/bind-key ,prefix ,prefix-map ,map)))
  93. ,@(mapcar (lambda (form)
  94. `(al/bind-key ,(car form) ,(cdr form)
  95. ,(or prefix-map map)))
  96. bindings))))
  97. (defmacro al/bind-keys* (&rest args)
  98. `(al/bind-keys :map al/override-global-map ,@args))
  99. ;;; Binding keys from maps
  100. (defvar al/default-keys-variables nil
  101. "Default list of variables used by `al/bind-keys-from-vars'.")
  102. (defun al/bind-keys-to-map (key-specs map-var)
  103. "Bind all keys from KEY-SPECS in MAP-VAR.
  104. KEY-SPECS is an alist of keybinding strings and functions (the
  105. same as the rest of arguments taken by `al/bind-keys').
  106. MAP-VAR is a variable with keymap."
  107. (al/with-check
  108. :var map-var
  109. (dolist (spec key-specs)
  110. (let ((key (car spec))
  111. (cmd (cdr spec)))
  112. (eval `(al/bind-key ,key ,cmd ,map-var))))))
  113. (defun al/keys-from-vars (vars)
  114. "Return list of key binding specifications from variables VARS.
  115. For the meaning of values of VARS, see `al/bind-keys-from-vars'.
  116. Returning value is an alist of keys and functions with removed
  117. key duplicates (rightmost values retain)."
  118. (let* ((vars (cl-remove-if-not #'al/bound? vars))
  119. (keys-raw (apply #'append
  120. (mapcar #'symbol-value vars)))
  121. (keys (mapcar #'al/list-maybe keys-raw)))
  122. (cl-remove-duplicates
  123. keys
  124. :test (lambda (obj1 obj2)
  125. (equal (car obj1) (car obj2))))))
  126. (defun al/bind-keys-from-vars (map-vars &optional key-vars no-default)
  127. "Bind all keys from KEY-VARS in all maps from MAP-VARS.
  128. MAP-VARS is a variable or a list of variables with keymaps.
  129. KEY-VARS is a variable or a list of variables with bindings.
  130. Each variable should contain a list of key bindings
  131. specifications. Each spec should be either a cons of a key
  132. string and a function, or a key string (the bound function is nil
  133. in the latter case).
  134. Variables from `al/default-keys-variables' are also used for
  135. binding, unless NO-DEFAULT is non-nil. The bindings from
  136. KEY-VARS have a priority over the bindings from these variables."
  137. (declare (indent 1))
  138. (let* ((key-vars (append (unless no-default al/default-keys-variables)
  139. (al/list-maybe key-vars)))
  140. (specs (al/keys-from-vars key-vars)))
  141. (al/funcall-or-dolist map-vars
  142. (lambda (map-var)
  143. (al/bind-keys-to-map specs map-var)))))
  144. ;;; Binding buffer local keys
  145. ;; Idea from <http://www.emacswiki.org/emacs/BufferLocalKeys>.
  146. (defvar-local al/local-map nil
  147. "Local keymap used by `al/bind-local-keys-from-vars'.")
  148. (defun al/bind-local-keys-from-vars (&rest vars)
  149. "Bind all keys from variables VARS locally in the current buffer.
  150. VARS are variables with bindings supported by
  151. `al/bind-keys-from-vars'."
  152. (setq al/local-map (copy-keymap (current-local-map)))
  153. (use-local-map al/local-map)
  154. (al/bind-keys-from-vars 'al/local-map vars t))
  155. ;;; Misc
  156. (defun al/clean-map (map-var)
  157. "Remove all key bindings from MAP-VAR variable with keymap."
  158. (al/with-check
  159. :var map-var
  160. (setcdr (symbol-value map-var) nil)))
  161. (provide 'al-key)
  162. ;;; al-key.el ends here