├── .gitignore
├── README.md
└── control-mode.el
/.gitignore:
--------------------------------------------------------------------------------
1 | *-autoloads.el
2 | *.elc
3 | *~
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Control Mode
2 | ============
3 |
4 | [](http://www.gnu.org/licenses/gpl-3.0.txt)
5 | [](http://melpa.org/#/control-mode)
6 |
7 | * [Installation](#installation)
8 | * [MELPA](#melpa)
9 | * [Manual Installation](#manual-installation)
10 | * [Setup](#setup)
11 | * [What It Does](#what-it-does)
12 | * [Examples](#examples)
13 | * [Regenerating Key Bindings](#regenerating-key-bindings)
14 | * [Tips](#tips)
15 | * [Customization](#customization)
16 | * [License](#license)
17 |
18 | Control Mode is a minor mode for Emacs that provides a “control” mode,
19 | similar in purpose to Vim's “normal” mode. Unlike the various Vim emulation
20 | modes, the key bindings in Control Mode are derived from the key bindings
21 | already setup, usually by making the control key unnecessary,
22 | e.g. C-f becomes f. This provides the power of a mode
23 | dedicated to controlling the editor without needing to learn or maintain new
24 | key bindings.
25 |
26 | Installation
27 | ------------
28 |
29 | ### MELPA
30 |
31 | If you haven't already, add the following lines to your `.emacs.d/init.el`
32 | and restart Emacs so that you can install packages from MELPA:
33 |
34 | ```emacs-lisp
35 | (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
36 | ```
37 |
38 | Then do M-x package-install RET control-mode RET.
39 |
40 | ### Manual Installation
41 |
42 | Download the `control-mode.el` file and put it in your `.emacs.d`
43 | directory. Add the following lines to your `.emacs.d/init.el`:
44 |
45 | ```emacs-lisp
46 | (add-to-list 'load-path "~/.emacs.d/")
47 | (require 'control-mode)
48 | ```
49 |
50 | ### Setup
51 |
52 | Once you have the control-mode package installed, you can add the following
53 | line to your `.emacs`:
54 |
55 | ```emacs-lisp
56 | (control-mode-default-setup)
57 | ```
58 |
59 | This will setup C-z to turn on Control Mode globally, and
60 | C-z and z to turn it off globally. If you prefer to
61 | use it on a buffer by buffer basis, use `control-mode-localized-setup`. If
62 | you need the usual binding for `Ctrl-z` to suspend Emacs, you can use
63 | `Ctrl-x Ctrl-z` instead (`x Ctrl-z` in Control Mode). It also binds `x f` to
64 | `find-file` (or whatever you had bound to `Ctrl-x Ctrl-f`) in Control Mode
65 | if it would otherwise be bound to `set-fill-column`.
66 |
67 | If you want to run `control-mode` globally as above but want to disable it for
68 | some specific modes, add the mode to the `global-control-mode-exceptions` custom
69 | variable.
70 |
71 | What It Does
72 | ------------
73 |
74 | Control Mode looks at every key binding you already have defined. For each
75 | binding that includes ⎈ Ctrl, it tries to rebind it without
76 | ⎈ Ctrl. It will only do this if the key binding it is replacing
77 | is unbound or bound to `self-insert-command` or `org-self-insert-command`,
78 | the Emacs commands for keys that simply enter themselves. It will also look
79 | at all bindings with ◆ Meta (⎇ Alt on most keyboards),
80 | and try to rebind those without the ◆ Meta. ⎈ Ctrl
81 | bindings take precedence over ◆ Meta bindings.
82 |
83 | An exception is made for C-m and C-i, which are
84 | usually synonyms for ↵ Enter and ↹ Tab in Emacs. They
85 | will be ignored, allowing M-m and M-i to be bound to
86 | m and i.
87 |
88 | C-M combinations also get rebound. C-M will get bound
89 | to ⎈ Ctrl if ⎈ Ctrl unbound or rebound, and to ◆
90 | Meta if ◆ Meta was unbound or rebound. If you set the
91 | variable `control-mode-rebind-to-shift` to `t` Control Mode will also try to
92 | rebind to ⇧ Shift if that binding wouldn't already be taken over
93 | by a ⎈ Ctrl + ⇧ Shift or ◆ Meta + ⇧
94 | Shift binding. This may interfere with the use of ⇧ Shift
95 | with movement commands to select a region however, and so is off by default.
96 |
97 | Control Mode does the right thing when a key binding includes modifiers
98 | other than ⎈ Ctrl and ◆ Meta. For example, it will
99 | rebind ⎈ Ctrl + ⇧ Shift + ⌫ Backspace to
100 | ⇧ Shift + ⌫ Backspace if ⇧ Shift + ⌫
101 | Backspace has a key binding it is allowed to replace, and it will try
102 | to rebind ⎈ Ctrl + ◆ Meta + Super +
103 | Hyper to ⎈ Ctrl + Super +
104 | Hyper + x.
105 |
106 | Control mode will recurse into prefix keys' keymaps, for example C-x
107 | C-x becomes available as x C-x and x x.
108 |
109 | ### Examples
110 |
111 | Suppose C-f, M-f, and C-M-f are all bound
112 | to commands but f is either unbound or just types “f”. Control
113 | mode would create key bindings like so:
114 |
115 |
116 | Original binding | Available in Control Mode as |
117 | C-f | f |
118 | M-f | M-f |
119 | C-M-f | C-f, C-M-f |
120 |
121 |
122 | If C-% isn't bound, but M-% and C-M-% are:
123 |
124 |
125 | Original binding | Available in Control Mode as |
126 | M-% | % |
127 |
128 | C-M-% |
129 | C-%, M-%, C-M-% |
130 |
131 |
132 |
133 | If M-n isn't bound, but C-n and C-M-n are:
134 |
135 |
136 | Original binding | Available in Control Mode as |
137 | C-n | n |
138 |
139 | C-M-n |
140 | C-n, M-n, C-M-n |
141 |
142 |
143 |
144 | ### Regenerating Key Bindings
145 |
146 | Control mode generates bindings separately for every combination of major
147 | mode and minor modes, and so will setup different bindings in each buffer as
148 | necessary. It is able to detect when the major mode changes and adapt to
149 | that, but there is no way for Control Mode to know if you have turned on a
150 | new minor mode. If this causes a problem, turn Control Mode off and back on
151 | again.
152 |
153 | If you change the key bindings in any of the modes or in your global keymap,
154 | you may have to tell Control Mode to regenerate its key bindings. This can
155 | be done with the `control-mode-reload-bindings` command.
156 |
157 | Tips
158 | ----
159 |
160 | C-[ in Emacs acts like pushing ⎋ Esc or holding down
161 | ◆ Meta. In Control mode [ has this behavior. So for
162 | example, [ f will do `forward-word`. Also, all the number keys,
163 | -, and u are rebound in Control Mode to set arguments
164 | for following commands. So 3 k deletes the last three lines and
165 | u [ a jumps back four sentences.
166 |
167 | C-q, `quoted-insert`, gives you a way to insert text while in
168 | Control Mode. This becomes q, so q t q e q x q t will
169 | enter “text”.
170 |
171 | Keyboard macros in Emacs record the actual key presses used while creating
172 | them, and so a keyboard macro created in Control Mode may not work outside
173 | of Control Mode, and vice versa. You can start a keyboard macro with
174 | C-0 C-z to force it to turn Control Mode off, or C-1
175 | C-z to force it to turn Control Mode on, to prevent problems with
176 | keyboard macros being executed in the wrong mode.
177 |
178 | Customization
179 | -------------
180 |
181 | Besides the `control-mode-rebind-to-shift` variable mentioned above, Control
182 | Mode provides a keymap and a hook you can use for customization. You can
183 | create key bindings in `control-mode-keymap` and have them available in
184 | Control Mode. These override any automatically generated key bindings.
185 |
186 | You can also use `add-hook` with `control-mode-keymap-generation-functions`
187 | to hook into the keymap generation system. Functions attached to this hook
188 | will be passed a single parameter, a keymap they can define bindings in to
189 | make them available in Control Mode. These functions will be called once for
190 | each combination of major mode and minor modes, and so let you customize
191 | Control Mode based on the other modes or key bindings that are present.
192 |
193 | You can use Emacs' customization interface to customize Control Mode:
194 | M-x customize-group control-mode RET.
195 |
196 | License
197 | -------
198 |
199 | Copyright © 2013–2015 Stephen Marsh
200 |
201 | Distributed under GNU GPL, version 3.
202 |
--------------------------------------------------------------------------------
/control-mode.el:
--------------------------------------------------------------------------------
1 | ;;; control-mode.el --- A "control" mode, similar to vim's "normal" mode
2 |
3 | ;; Copyright (C) 2013 Stephen Marsh
4 |
5 | ;; Author: Stephen Marsh
6 | ;; Version: 0.1
7 | ;; URL: https://github.com/stephendavidmarsh/control-mode
8 | ;; Keywords: convenience emulations
9 |
10 | ;; Control mode is free software; you can redistribute it and/or modify it
11 | ;; under the terms of the GNU General Public License as published by
12 | ;; the Free Software Foundation; either version 3, or (at your option)
13 | ;; any later version.
14 | ;;
15 | ;; Control mode is distributed in the hope that it will be useful, but WITHOUT
16 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
18 | ;; License for more details.
19 | ;;
20 | ;; You should have received a copy of the GNU General Public License
21 | ;; along with Control mode. If not, see .
22 |
23 | ;;; Commentary:
24 |
25 | ;; Control mode is a minor mode for Emacs that provides a
26 | ;; "control" mode, similar in purpose to vim's "normal" mode. Unlike
27 | ;; the various vim emulation modes, the key bindings in Control mode
28 | ;; are derived from the key bindings already setup, usually by making
29 | ;; the control key unnecessary, e.g. "Ctrl-f" becomes "f". This
30 | ;; provides the power of a mode dedicated to controlling the editor
31 | ;; without needing to learn or maintain new key bindings. See
32 | ;; https://github.com/stephendavidmarsh/control-mode for complete
33 | ;; documentation.
34 |
35 | ;;; Code:
36 |
37 | (defvar control-mode-overrideable-bindings '(nil self-insert-command org-self-insert-command undefined))
38 |
39 | ;; Ignore Control-m and Control-i
40 | (defvar control-mode-ignore-events '(13 9))
41 |
42 | ;; This can't be control-mode-map because otherwise the
43 | ;; define-minor-mode will try to install it as the minor mode keymap,
44 | ;; despite being told the minor mode doesn't have a keymap.
45 | (defvar control-mode-keymap (make-sparse-keymap))
46 |
47 | (defvar control-mode-keymap-generation-functions)
48 |
49 | (defvar control-mode-conversion-cache)
50 |
51 | (defvar control-mode-emulation-alist nil)
52 | (make-variable-buffer-local 'control-mode-emulation-alist)
53 |
54 | (defvar control-mode-rebind-to-shift)
55 |
56 | (defvar global-control-mode-exceptions)
57 |
58 | (defun control-mode-create-alist ()
59 | (setq control-mode-emulation-alist
60 | (let* ((mode-key (cons major-mode (sort (mapcar (lambda (x) (car (rassq x minor-mode-map-alist))) (current-minor-mode-maps)) 'string<)))
61 | (value (assoc mode-key control-mode-conversion-cache)))
62 | (if value (cdr value)
63 | (let ((newvalue (mapcar (lambda (x) (cons t x)) (cons (control-mode-create-hook-keymap) (cons control-mode-keymap (mapcar (lambda (k) (control-mode-get-converted-keymap-for k (make-sparse-keymap) nil)) (current-active-maps)))))))
64 | (push (cons mode-key newvalue) control-mode-conversion-cache)
65 | newvalue)))))
66 |
67 | (defun control-mode-create-hook-keymap ()
68 | (let ((keymap (make-sparse-keymap)))
69 | (run-hook-with-args 'control-mode-keymap-generation-functions keymap)
70 | keymap))
71 |
72 | (defun control-mode-get-converted-keymap-for (keymap auto-keymap prefix)
73 | ;; Namespaces? Classes? Inner functions? What's that?
74 | (cl-flet*
75 | ((add-binding (e b) (define-key auto-keymap (vector e) b))
76 | (key-bindingv (e) (key-binding (vconcat (reverse (cons e prefix)))))
77 | (mod-modifiers (e f) (event-convert-list (append (funcall f (event-modifiers e)) (list (event-basic-type e)))))
78 | (remove-modifier (e mod) (mod-modifiers e (lambda (x) (remq mod x))))
79 | (add-modifier (e mod) (mod-modifiers e (lambda (x) (cons mod x))))
80 | (add-modifiers (e mod1 mod2) (mod-modifiers e (lambda (x) (cons mod2 (cons mod1 x)))))
81 | (is-mouse (mods) (or (memq 'click mods) (memq 'down mods)))
82 | (is-overrideable (b) (memq (key-bindingv b) control-mode-overrideable-bindings))
83 | (try-to-rebind (e b) (if (not (is-overrideable e)) nil
84 | (add-binding e b)
85 | t))
86 | (convert-keymap (e b) (if (not (keymapp b)) b
87 | (let ((new-auto-keymap (make-sparse-keymap)))
88 | (set-keymap-parent new-auto-keymap b)
89 | (control-mode-get-converted-keymap-for
90 | b new-auto-keymap (cons e prefix)))))
91 | (handle-escape-binding
92 | (event binding)
93 | (unless (memq event control-mode-ignore-events)
94 | (let ((newbinding (convert-keymap event binding))
95 | (eventmods (event-modifiers event)))
96 | (unless (is-mouse eventmods)
97 | (if (keymapp binding) (add-binding (add-modifier event 'meta) newbinding))
98 | (if (memq 'control eventmods)
99 | (let ((only-meta (add-modifier (remove-modifier event 'control) 'meta))
100 | (only-shift (add-modifier (remove-modifier event 'control) 'shift)))
101 | (try-to-rebind only-meta newbinding)
102 | (try-to-rebind event newbinding)
103 | (if (and control-mode-rebind-to-shift
104 | (not (memq 'shift eventmods))
105 | (not (key-bindingv (add-modifier only-shift 'control)))
106 | (not (key-bindingv (add-modifier only-shift 'meta))))
107 | (try-to-rebind only-shift newbinding)))
108 | (let ((control-instead (add-modifier event 'control)))
109 | (when (and (is-overrideable event)
110 | (or (not (key-bindingv control-instead))
111 | (memq control-instead control-mode-ignore-events)))
112 | (add-binding event newbinding)
113 | (if (not (memq control-instead control-mode-ignore-events))
114 | (let* ((cmevent (add-modifiers event 'control 'meta))
115 | (cmbinding (convert-keymap cmevent (key-bindingv cmevent))))
116 | (when cmbinding
117 | (add-binding (add-modifier event 'meta) cmbinding)
118 | (add-binding control-instead cmbinding)))))))))))
119 | (handle-binding
120 | (event binding)
121 | (unless (memq event control-mode-ignore-events)
122 | (let ((eventmods (event-modifiers event)))
123 | (if (and (memq 'control eventmods) (not (is-mouse eventmods)))
124 | (if (and (eq event 27)
125 | (keymapp binding))
126 | (progn
127 | (map-keymap (function handle-escape-binding) binding)
128 | (try-to-rebind 91 binding))
129 | (let ((newevent (remove-modifier event 'control))
130 | (newbinding (convert-keymap event binding)))
131 | (if (keymapp binding) (add-binding event newbinding))
132 | (when (try-to-rebind newevent newbinding)
133 | (unless (memq 'meta eventmods) ; Here to be safe, but Meta events should be inside Escape keymap
134 | (let* ((cmevent (add-modifier event 'meta))
135 | (cmbinding (convert-keymap cmevent (key-bindingv cmevent))))
136 | (when cmbinding
137 | (add-binding event cmbinding))))))))))))
138 | (map-keymap (function handle-binding) keymap)
139 | auto-keymap))
140 |
141 | ;;;###autoload
142 | (define-minor-mode control-mode
143 | "Toggle Control mode.
144 | With a prefix argument ARG, enable Control mode if ARG
145 | is positive, and disable it otherwise. If called from Lisp,
146 | enable the mode if ARG is omitted or nil.
147 |
148 | Control mode is a global minor mode."
149 | nil " Control" nil (if control-mode (control-mode-setup) (control-mode-teardown)))
150 |
151 | ;;;###autoload
152 | (define-globalized-minor-mode global-control-mode control-mode
153 | (lambda ()
154 | (unless (apply 'derived-mode-p global-control-mode-exceptions)
155 | (control-mode))))
156 |
157 | (add-hook 'emulation-mode-map-alists 'control-mode-emulation-alist)
158 |
159 | (defun control-mode-setup ()
160 | (unless (string-prefix-p " *Minibuf" (buffer-name))
161 | (setq control-mode-emulation-alist nil)
162 | (control-mode-create-alist)))
163 |
164 | (defun control-mode-teardown ()
165 | (setq control-mode-emulation-alist nil))
166 |
167 | ;;;###autoload
168 | (defun control-mode-default-setup ()
169 | (define-key control-mode-keymap (kbd "C-z") 'global-control-mode)
170 | (global-set-key (kbd "C-z") 'global-control-mode)
171 | (add-hook 'control-mode-keymap-generation-functions
172 | 'control-mode-ctrlx-hacks))
173 |
174 | ;;;###autoload
175 | (defun control-mode-localized-setup ()
176 | (define-key control-mode-keymap (kbd "C-z") 'control-mode)
177 | (global-set-key (kbd "C-z") 'control-mode)
178 | (add-hook 'control-mode-keymap-generation-functions
179 | 'control-mode-ctrlx-hacks))
180 |
181 | (defun control-mode-ctrlx-hacks (keymap)
182 | (if (eq (key-binding (kbd "C-x f")) 'set-fill-column)
183 | (define-key keymap (kbd "x f") (lookup-key (current-global-map) (kbd "C-x C-f")))))
184 |
185 | (defun control-mode-reload-bindings ()
186 | "Force Control mode to reload all generated keybindings."
187 | (interactive)
188 | (setq control-mode-conversion-cache nil)
189 | (mapc (lambda (buf)
190 | (with-current-buffer buf
191 | (if control-mode
192 | (control-mode-setup))))
193 | (buffer-list)))
194 |
195 | (defcustom control-mode-rebind-to-shift nil
196 | "Allow rebinding Ctrl-Alt- to Shift-"
197 | :group 'control
198 | :type '(boolean)
199 | :set (lambda (x v)
200 | (setq control-mode-rebind-to-shift v)
201 | (control-mode-reload-bindings)))
202 |
203 | (defcustom global-control-mode-exceptions '()
204 | "List of modes to exclude for `global-control-mode'."
205 | :group 'control
206 | :type '(repeat (function))
207 | :set (lambda (x v)
208 | (setq global-control-mode-exceptions v)))
209 |
210 | (provide 'control-mode)
211 |
212 | ;;; control-mode.el ends here
213 |
--------------------------------------------------------------------------------