Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

218 lines
8.2 KiB

  1. ;;; free-keys.el --- Show free keybindings for modkeys or prefixes
  2. ;; Copyright (C) 2013 Matus Goljer
  3. ;; Author: Matus Goljer <matus.goljer@gmail.com>
  4. ;; Maintainer: Matus Goljer <matus.goljer@gmail.com>
  5. ;; Version: 0.1
  6. ;; Package-Version: 1.0.0
  7. ;; Package-Commit: edfd69dc369b2647447b7c28c7c1163b1ddf45b4
  8. ;; Created: 3rd November 2013
  9. ;; Keywords: convenience
  10. ;; Package-Requires: ((cl-lib "0.3"))
  11. ;; URL: https://github.com/Fuco1/free-keys
  12. ;; This file is not part of GNU Emacs.
  13. ;; This program is free software; you can redistribute it and/or modify
  14. ;; it under the terms of the GNU General Public License as published by
  15. ;; the Free Software Foundation, either version 3 of the License, or
  16. ;; (at your option) any later version.
  17. ;; This program is distributed in the hope that it will be useful,
  18. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. ;; GNU General Public License for more details.
  21. ;; You should have received a copy of the GNU General Public License
  22. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. ;;; Commentary:
  24. ;; Show free keybindings for modkeys or prefixes. Based on code
  25. ;; located here: https://gist.github.com/bjorne/3796607
  26. ;;
  27. ;; For complete description see https://github.com/Fuco1/free-keys
  28. ;;; Code:
  29. (require 'cl-lib)
  30. (defgroup free-keys ()
  31. "Free keys."
  32. :group 'convenience)
  33. (defcustom free-keys-modifiers '("" "C" "M" "C-M")
  34. "List of modifiers that can be used in front of keys."
  35. :type '(repeat string)
  36. :group 'free-keys)
  37. (defcustom free-keys-keys "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()-=[]{};'\\:\"|,./<>?`~"
  38. "String or list of keys that can be used as bindings.
  39. In case of string, each letter is interpreted as a character to
  40. test.
  41. In case of list, each item is considered as key code. This
  42. allows you to add keys such as TAB or RET."
  43. :type '(choice
  44. (string :tag "String of characters")
  45. (repeat :tag "List of characters" string))
  46. :group 'free-keys)
  47. (defcustom free-keys-ignored-bindings nil
  48. "List of bindings with modifiers which should never be considered free.
  49. The elements could be either strings of form \"MOD-KEY\" or cons
  50. where the car is a single letter modifier as in
  51. `free-keys-modifiers' and the cdr is a string containing keys to
  52. be ignored with this modifiers, like `free-keys-keys'.
  53. The bindings should not contain a prefix. This can typically be
  54. used to ignore bindings intercepted by the window manager used
  55. for swapping windows and similar operations."
  56. :type '(repeat (choice (string :tag "Key binding")
  57. (cons :tag "Modifier and string of key bindings"
  58. (string :tag "Modifier")
  59. (string :tag "Key bindings"))))
  60. :group 'free-keys)
  61. (defun free-keys-ignored-bindings ()
  62. "Return a list of bindings that should never be considered free.
  63. The elements of the returned list are of form \"MOD-KEY\".
  64. See also the variable `free-keys-ignored-bindings'."
  65. (apply 'append
  66. (mapcar (lambda (x)
  67. (if (stringp x) (list x)
  68. (mapcar (lambda (y)
  69. (concat (car x) "-" (char-to-string y)))
  70. (cdr x))))
  71. free-keys-ignored-bindings)))
  72. (defvar free-keys-mode-map
  73. (let ((map (make-keymap)))
  74. (define-key map "b" 'free-keys-change-buffer)
  75. (define-key map "p" 'free-keys-set-prefix)
  76. map)
  77. "Keymap for Free Keys mode.")
  78. (defvar free-keys-original-buffer nil
  79. "Buffer from which `free-keys' was called.")
  80. (defun free-keys--print-in-columns (key-list &optional columns)
  81. "Print the KEY-LIST into as many columns as will fit into COLUMNS characters.
  82. The columns are ordered according to variable `free-keys-keys',
  83. advancing down-right. The margin between each column is 5 characters."
  84. (setq columns (or columns 80))
  85. (let* ((len (+ 5 (length (car key-list))))
  86. (num-of-keys (length key-list))
  87. (cols (/ columns len))
  88. (rows (1+ (/ num-of-keys cols)))
  89. (rem (mod num-of-keys cols))
  90. (cur-col 0)
  91. (cur-row 0))
  92. (dotimes (i num-of-keys)
  93. (insert (nth
  94. (+ (* cur-col rows) cur-row (if (> cur-col rem) (- rem cur-col) 0))
  95. key-list)
  96. " ")
  97. (cl-incf cur-col)
  98. (when (= cur-col cols)
  99. (insert "\n")
  100. (setq cur-col 0)
  101. (cl-incf cur-row)))))
  102. (defun free-keys-set-prefix (prefix)
  103. "Change the prefix in current *Free keys* buffer to PREFIX and
  104. update the display."
  105. (interactive "sPrefix: ")
  106. (free-keys prefix free-keys-original-buffer))
  107. (defun free-keys-change-buffer (buffer)
  108. "Change the buffer for which the bindings are displayed to
  109. BUFFER and update the display."
  110. (interactive "bShow free bindings for buffer: ")
  111. (free-keys nil (get-buffer-create buffer)))
  112. (defun free-keys-revert-buffer (_ignore-auto _noconfirm)
  113. "Revert the *Free keys* buffer.
  114. This simply calls `free-keys'."
  115. (free-keys nil free-keys-original-buffer))
  116. (defun free-keys--process-modifier (prefix modifier)
  117. "Process free bindings for MODIFIER."
  118. (let (empty-keys)
  119. (mapc (lambda (key)
  120. (let* ((key-as-string (cond
  121. ((characterp key) (char-to-string key))
  122. ((stringp key) key)
  123. (t (error "Key is not a character nor a string"))))
  124. (key-name
  125. (if (not (equal modifier ""))
  126. (concat modifier "-" key-as-string)
  127. key-as-string))
  128. (full-name
  129. (if (and prefix (not (equal prefix ""))) (concat prefix " " key-name) key-name))
  130. (binding
  131. (with-current-buffer free-keys-original-buffer (key-binding (read-kbd-macro full-name)))))
  132. (when (and (not (member key-name (free-keys-ignored-bindings)))
  133. (or (not binding)
  134. (eq binding 'undefined)))
  135. (push full-name empty-keys))))
  136. free-keys-keys)
  137. (let ((len (length empty-keys)))
  138. (when (> len 0)
  139. (if (not (equal modifier ""))
  140. (insert (format "With modifier %s (%d free)\n=========================\n" modifier len))
  141. (insert (format "With no modifier (%d free)\n=========================\n" len)))
  142. (free-keys--print-in-columns (nreverse empty-keys))
  143. (insert "\n\n")))))
  144. ;;;###autoload
  145. (defun free-keys (&optional prefix buffer)
  146. "Display free keys in current buffer.
  147. A free key is a key that has no associated key-binding as
  148. determined by function `key-binding'.
  149. By default, keys on `free-keys-keys' list with no prefix sequence
  150. are considered, possibly together with modifier keys from
  151. `free-keys-modifiers'. You can change the prefix sequence by
  152. hitting 'p' in the *Free keys* buffer. Prefix is supplied in
  153. format recognized by `kbd', for example \"C-x\"."
  154. (interactive (list (when current-prefix-arg
  155. (read-from-minibuffer "Prefix: "))))
  156. (setq prefix (or prefix ""))
  157. (setq free-keys-original-buffer (or buffer (current-buffer)))
  158. (let ((buf (get-buffer-create "*Free keys*")))
  159. (pop-to-buffer buf)
  160. (with-current-buffer buf
  161. (if (fboundp 'read-only-mode)
  162. (read-only-mode -1)
  163. (setq buffer-read-only nil))
  164. (erase-buffer)
  165. (insert "Free keys"
  166. (if (not (equal prefix "")) (format " with prefix %s" prefix) "")
  167. " in buffer "
  168. (buffer-name free-keys-original-buffer)
  169. " (major mode: " (with-current-buffer free-keys-original-buffer (symbol-name major-mode)) ")\n\n")
  170. (mapc (lambda (m) (free-keys--process-modifier prefix m)) free-keys-modifiers)
  171. (setq buffer-read-only t)
  172. (goto-char 0)
  173. (free-keys-mode))))
  174. (define-derived-mode free-keys-mode special-mode "Free Keys"
  175. "Free keys mode.
  176. Display the free keybindings in current buffer.
  177. \\{free-keys-mode-map}"
  178. (set (make-local-variable 'revert-buffer-function) 'free-keys-revert-buffer)
  179. (set (make-local-variable 'header-line-format) "Help: (b) change buffer (p) change prefix (q) quit"))
  180. (provide 'free-keys)
  181. ;;; free-keys.el ends here