Emacs: Translating C-h to DEL, M-h to M-DEL - emacs

I want my C/M-h keys to be a binding for delete.
The usual solution of global-set-key doesn't work for me, as I want these keys to behave as delete everywhere including the minibuffer and various modes, and be able to work between my various Linux/OS X/Windows emacs installations.
I have the following near solution:
(when (>= emacs-major-version 23)
(setq help-char (string-to-char "<f1>")) ;; Bind ONLY F1 to help, not C-h
(define-key input-decode-map (kbd "C-h") (kbd "DEL")) ;; Translate C-h to DEL ;; FIXME: Should be in emacs 22, doens't work.
(define-key input-decode-map (kbd "M-h") (kbd "M-<DEL>")) ;; Translate M-h to M-DEL
;; (define-key input-decode-map (kbd "<backspace>") 'version) ;; Stop me using backspace for a while
)
But this obviously only works with Emacs > 22, due to input-decode-map.
Would someone be able to help me find a solution that works in 22, maybe even 21? (Not a priority however). Cheers!
Edit: My Solution:
It's not quite there yet, but this has solved most of my issues:
(setq help-char [f1]) ;; I don't want help when I'm just trying to backspace!
(define-key isearch-mode-map "\C-h" 'isearch-delete-char)
;; (define-key isearch-mode-map "\M-h" 'my-isearch-delete-word)
(defvar my-overriding-binding-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [?\C-h] 'delete-backward-char)
(define-key map [?\M-h] 'backward-kill-word)
map))
(define-minor-mode my-overriding-binding-mode
"Personal global key-bindings."
:global t)
(my-overriding-binding-mode 1)
There's probably some caveats for some misbehaving modes that need to be written - I'll do that as I come across them. The only real problem now seems to be M-h in isearch, which I'll post as a seperate question.
Thanks again guys, you've been a great help.

Here's the first, and most important, part of the answer, with the explanations all given in the elisp comments. This has been tested mostly in X11 environments, but including with a Mac keyboard. It has been tested starting with v19.28, and up to and including v23.3. I think it works with v24, but I have not yet used v24 very much.
You can find my complete (though not always 100% up-to-date) ~/.emacs.el in the following repository of initialization and configuration files:
https://github.com/robohack/dotfiles
and so specifically ~/.emacs.el is at:
https://github.com/robohack/dotfiles/blob/master/.emacs.el
;;; first off, we do some fancy stuff to make C-h work "properly," but still
;;; have good access to the help functions!
;;;
;;; Using C-h for "help" might seem OK to some folks, but since it's also the
;;; ASCII standard value for the "backspace" character, one typically used ever
;;; since the days of the typewriter to move the cursor backwards one position
;;; and in computing normally to erase any character backed over, a vast amount
;;; of stupidity is needed in emacs to continue to (ab)use as the "help"
;;; character. Instead it is still quite intuitive, and often much easier in
;;; zillions of environments, to use M-? for help.
;;;
;;; So, we can set C-h and C-? and friends to sensible bindings...
;;
;; Remember to call override-local-key-settings in the appropriate hooks to fix
;; up modes which violate global user preferences....
;;
(global-set-key "\C-h" 'delete-backward-char)
(global-set-key "\C-?" 'delete-char)
(global-set-key "\e\C-h" 'backward-kill-word)
(global-set-key "\e\C-?" 'kill-word)
;;; and then we diddle with help to make it work again....
;;
;; Oddly, the help interface in emacs is extremely scatter-brained, with
;; several slightly different ways of doing the same thing. This is probably
;; due to the fact that several different programmers have implemented various
;; bits and pieces of the help systems. See help.el and help-macro.el, but try
;; not to tear your hair out when you find out help-event-list in 19.34 is
;; essentially bogus, since it is simply an extension to a "standard" list.
;;
;; Remember to call override-local-key-settings in the appropriate hooks to fix
;; up modes which violate global user preferences....
;;
(global-set-key [f1] 'help-command) ; first do this for 19.28.
(global-set-key "\e?" 'help-command) ; this is the first step to set up help
(global-set-key "\e?F" 'view-emacs-FAQ) ; in 19.34 it needs more help...
;; should help-char be just ? instead?
(setq help-char ?\M-?) ; this should "fix" the rest.
;; one more handy help-related binding...
;;
(define-key help-map "?" 'describe-key-briefly) ; also C-x? for Jove compat
;;; Now for function key mappings...
;;;
;;; I USUALLY EXPECT THE BACKSPACE KEY TO WORK LIKE AN ASCII BACKSPACE!
;;
;; For some entirely un-fathomable reason the default function bindings make
;; the 'backspace' and 'delete' keys synonymous!
;;
;; NOTE: this *should* work by simply reading termio for current erase char.
;;
;; As of emacs-21.2 a note was added to the NEWS file which says "** On
;; terminals whose erase-char is ^H (Backspace), Emacs now uses
;; normal-erase-is-backspace-mode." Unfortunately this does EXACTLY the WRONG
;; thing, and in a totally bizzare, disruptive, subversive, and stupid
;; backwards way. With every major release it's gotten worse and worse and
;; worse; more convoluted, and ugly.
;;
;; So, we must do something to kill that horrible stupid broken poor
;; useless excuse for a feature, normal-erase-is-backspace-mode....
;;
;; seems 23.1 changes function-key-map radically....
;;
;; Unfortunately 23.1 also still has function-key-map so we can't make that
;; (function-key-map) an alias for the new local-function-key-map that we need
;; to use in 23.1 to modify key translations. Sigh.
;;
;; Instead make a new alias that can be used transparently as the desired map.
;;
(eval-and-compile
(if (functionp 'defvaralias) ; since 22.1
(if (boundp 'local-function-key-map)
(defvaralias 'my-function-key-map 'local-function-key-map
"Special variable alias to allow transparent override of
`local-function-key-map' for 23.1 vs 22.3(?).")
(defvaralias 'my-function-key-map 'function-key-map
"Special variable alias to allow transparent override
of `function-key-map' for 22.3(?) vs. older,"))
;; XXX is this right? it works (maybe?)
(defvar my-function-key-map function-key-map)))
;;
;; First undo (local-)function-key-map weirdness.
;;
;; luckily on Mac OS-X X11, at least with the mini-wireless keyboard and on the
;; large USB keyboard, the big "delete" key on the main block is actually
;; sending <backspace> by default, else one would have to first change the X11
;; keyboard map!
;;
(define-key my-function-key-map [delete] [?\C-?])
(define-key my-function-key-map [S-delete] [?\C-h])
(define-key my-function-key-map [M-delete] [?\C-\M-?])
(define-key my-function-key-map [kp-delete] [?\C-?])
(define-key my-function-key-map [backspace] [?\C-h])
(define-key my-function-key-map [S-backspace] [?\C-?])
;;(define-key my-function-key-map [C-backspace] [?\C-h]) ; sometimes *is* DEL....
(define-key my-function-key-map [M-backspace] [?\e?\C-h])
(define-key my-function-key-map [M-S-backspace] [?\e?\C-?])
(define-key my-function-key-map [kp-backspace] [?\C-h])
;;
;; Next, zap the keyboard translate table, set up by
;; normal-erase-is-backspace-mode (in simple.el), which can do nothing
;; but confuse!
;;
(setq keyboard-translate-table nil)
;;
;; Finally, kill, Kill, KILL! the input-decode-map added in 23.x, and set
;; up by normal-erase-is-backspace-mode (in simple.el) which can do
;; nothing but confuse!
;;
;; This is TRULY _E_V_I_L_!!!! HORRID!!! MASSIVELY STUPID!!!!
;;
;; input-decode-map is poorly documented, and causes things above and
;; below to fail with the most confusing errors!
;;
;; (This probably only needs to be blown away on window systems, and
;; perhaps only for X, but doing it here now is apparently early enough
;; to allow for terminal mode specific settings to be re-applied to it
;; and so it seems safe to just blow away the asinine stupid attempt to
;; transpose backspace and delete. RMS is a pedantic idiot on this!)
;;
(if (boundp 'input-decode-map)
(setq input-decode-map (make-sparse-keymap)))
;; finally here's a little function to help fix up modes which don't honour default
;; bindings in sensible ways. Use this in any init hooks for modes which cause problems
;;
(defun override-local-key-settings ()
"User defined function. Intended to be called within various hooks to
override the value of buffer-local key map settings which may have been
overridden without consideration by the major mode."
(local-set-key "\C-?" 'delete-char) ; many modes
(local-set-key "\C-h" 'delete-backward-char) ; sh-mode
;; the rest are *not* overridden by cc-mode, but are by c-mode
(local-set-key "\e\C-h" 'backward-kill-word) ; text-mode
(local-set-key "\e?" 'help-command) ; nroff-mode
(local-set-key "\eh" 'mark-c-function)
(local-set-key "\e\C-?" 'kill-word)
(local-set-key "\e\C-e" 'compile)
;; try this on for size...
(local-set-key "\C-x\e\C-e" 'recompile)
)
(add-hook 'isearch-mode-hook
(function
(lambda ()
"Private isearch-mode fix for C-h."
(define-key isearch-mode-map "\C-h" 'isearch-delete-char))))
;;; OK, that's the end of the stuff to fix GNU Emacs' C-h brain damage. Phew!

Then I recommend you use a minor-mode:
(defvar my-overriding-binding-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [?\C-h] 'delete-backward-char)
(define-key map [?\M-h] 'backward-kill-word)
map))
(define-minor-mode my-overriding-binding-mode
"Personal global key-bindings."
:global t)
(my-overriding-binding-mode 1)

Related

Minor Mode Conditional Emacs Shortcuts

I have an emacs shortcut set up like so:
;; Adding the key mappings to minor mode.
(defvar my-keys-minor-mode-map (make-keymap) "my-keys-minor-mode keymap.")
;; Cancel with one press of escape instead of three.
(define-key my-keys-minor-mode-map (kbd "<escape>") 'keyboard-quit)
(my-keys-minor-mode 1)
(defconst my-minor-mode-alist (list (cons 'my-keys-minor-mode
my-keys-minor-mode-map)))
(setf emulation-mode-map-alists '(my-minor-mode-alist))
This works great to override a shortcut in a truly global way, superseding all other minor modes. However I need a way to have a shortcut behave differently in different minor modes. For example I would like the escape key to run helm-keyboard-quit when helm is running bu run keyboard-quit otherwise. Doing so like:
(eval-after-load "helm"
'(progn
(define-key helm-map (kbd "<escape>") 'helm-keyboard-quit)))
won't work because the global escape shortcut will override it. Is there a way to do this?
Why not use a simple lambda. Something like this (untested):
(global-set-key (kbd "<escape>")
(lambda()
(interactive)
(if helm-mode (helm-keyboard-quit)
(keyboard-quit))))

binding the escape key to a keychord for evil-mode

I have taken the below code from emacs site for evil -
(defun my-esc (prompt)
"Functionality for escaping generally. Includes exiting Evil insert state and C-g binding. "
(cond
;; If we're in one of the Evil states that defines [escape] key, return [escape] so as
;; Key Lookup will use it.
((or (evil-insert-state-p) (evil-normal-state-p) (evil-replace-state-p) (evil-visual-state-p)) [escape])
;; This is the best way I could infer for now to have C-c work during evil-read-key.
;; Note: As long as I return [escape] in normal-state, I don't need this.
;;((eq overriding-terminal-local-map evil-read-key-map) (keyboard-quit) (kbd ""))
(t (kbd "C-g"))))
(define-key key-translation-map (kbd "C-c") 'my-esc)
;; Works around the fact that Evil uses read-event directly when in operator state, which
;; doesn't use the key-translation-map.
(define-key evil-operator-state-map (kbd "C-c") 'keyboard-quit)
;; Not sure what behavior this changes, but might as well set it, seeing the Elisp manual's
;; documentation of it.
(set-quit-char "C-c")
It sets up the C-c key for escaping from the insert mode. How do I change it to a more convenient keychord such as "tt" ?
I used the below -
(key-chord-define evil-insert-state-map "tt" 'evil-normal-state)
However when I press 'tt' in the insert mode, it gives the following msg in the mini buffer -
<key-chord> <escape> is undefined
I couldn't reproduce your error, works fine for me.
Make sure you have the key-chord package installed.
(require 'key-chord)(key-chord-mode 1) ; turn on key-chord-mode
(key-chord-define evil-insert-state-map "tt" 'evil-normal-state)

Remove major mode key bindings in emacs

I installed a new major mode (sunrise commander), and I want to reset all its predefined key bindings.
Although I can use
(add-hook 'sr-mode-hook
'(lambda ()
(define-key sr-mode-map "KEY" nil)
...
))
this mode have so many bindings, it's a tag tedious to my taste.
Is there a way to completely reset the key bindings of this major mode in a one-liner or few-liners?
EDIT #1:
I tried using these methods as described below:
(eval-after-load "sunrise"
'(setq sr-mode-map (make-sparse-keymap)))
OR
(add-hook 'sr-mode-hook
(lambda ()
(setq sr-mode-map (make-sparse-keymap))))
Sadly, neither of them seems to work.
Do I actually need to define a new, empty keymap?
E.g. using
(defvar sunrise-keys-mode-map (make-keymap) "sunrise-keys-mode keymap.")
(define-minor-mode sunrise-keys-mode
"A minor mode so that my key settings override sunrise major mode keymap."
t " my-keys" 'sunrise-keys-mode-map)
(sunrise-keys-mode 1)
(eval-after-load "sunrise" ;; Fix this line to include the correct library name
'(setq sr-mode-map (sunrise-keys-mode)))
EDIT #2:
After a bit of tinkering in the sunrise commander code, I noticed that the sr-mode-map is based on the dired mode map. I disabled both, and it worked perfectly.
(eval-after-load "sunrise-commander"
'(setq sr-mode-map (make-sparse-keymap)
dired-mode-map (make-sparse-keymap)))
For future reference - the above is the only code needed.
make-sparse-keymap is a function that returns an empty keymap (unless provided with an argument, apparently).
You cound bind sr-mode-map to a newly-created, empty keymap:
(setq sr-mode-map (make-sparse-keymap))
You might need to delay this until after sunrise commander is loaded:
(eval-after-load "sc" ;; Fix this line to include the correct library name
'(setq sr-mode-map (make-sparse-keymap)))

eval-after-load vs. mode hook

Is there a difference between setting things for a mode using eval-after-load and using the mode hook?
I've seen some code where define-key is used inside a major mode hook, and some other code where define-key is used in eval-after-load form.
Update:
For better understanding, here is an example of using eval-after-load and mode hooks with org-mode. The code can run before (load "org") or (require 'org) or (package-initialize).
;; The following two lines of code set some org-mode options.
;; Usually, these can be outside (eval-after-load ...) and work.
;; In cases that doesn't work, try using setq-default or set-variable
;; and putting them in (eval-after-load ...), if the
;; doc for the variables don't say what to do.
;; Or use Customize interface.
(setq org-hide-leading-stars t)
(setq org-return-follows-link t)
;; "org" because C-h f org-mode RET says that org-mode is defined in org.el
(eval-after-load "org"
'(progn
;; Establishing your own keybindings for org-mode.
;; Variable org-mode-map is available only after org.el or org.elc is loaded.
(define-key org-mode-map (kbd "<C-M-return>") 'org-insert-heading-respect-content)
(define-key org-mode-map (kbd "<M-right>") nil) ; erasing a keybinding.
(define-key org-mode-map (kbd "<M-left>") nil) ; erasing a keybinding.
(defun my-org-mode-hook ()
;; The following two lines of code is run from the mode hook.
;; These are for buffer-specific things.
;; In this setup, you want to enable flyspell-mode
;; and run org-reveal for every org buffer.
(flyspell-mode 1)
(org-reveal))
(add-hook 'org-mode-hook 'my-org-mode-hook)))
Code wrapped in eval-after-load will be executed only once, so it is typically used to perform one-time setup such as setting default global values and behaviour. An example might be setting up a default keymap for a particular mode. In eval-after-load code, there's no notion of the "current buffer".
Mode hooks execute once for every buffer in which the mode is enabled, so they're used for per-buffer configuration. Mode hooks are therefore run later than eval-after-load code; this lets them take actions based upon such information as whether other modes are enabled in the current buffer.

Code completion key bindings in Emacs

When doing a M-x describe-mode in a .el file, I noticed that the Emacs-Lisp mode actually does code completion. However, lisp-complete-symbol is bound to M-TAB. In Windows, this key binding is taken by Windows for switching the active window. Most IDE's use C-SPC, but that's taken in Emacs as well. What is a good, fairly common key binding for code completion?
If you like completion of all kinds, I recommend M-/ and binding that to hippie-expand.
(global-set-key (kbd "M-/") 'hippie-expand)
It does a variety of completions, which are controlled by the variable hippie-expand-try-functions-list. In the .el files, you can set that to do the 'try-complete-lisp-symbol first to get the behavior you're asking for above, along with all the other expansions hippie-expand provides.
This would do that for you:
(add-hook 'emacs-lisp-mode-hook 'move-lisp-completion-to-front)
(defun move-lisp-completion-to-front ()
"Adjust hippie-expand-try-functions-list to have lisp completion at the front."
(make-local-variable 'hippie-expand-try-functions-list)
(setq hippie-expand-try-functions-list
(cons 'try-complete-lisp-symbol
(delq 'try-complete-lisp-symbol hippie-expand-try-functions-list)))
(setq hippie-expand-try-functions-list
(cons 'try-complete-lisp-symbol-partially
(delq 'try-complete-lisp-symbol-partially hippie-expand-try-functions-list))))
As Trey Jackson mentioned, hippie-expand is the way to go, but along with binding it to M-/, I also like having the TAB key do all my completion work for me. So I have this from the Emacs-Wiki in my .emacs file:
;;function to implement a smarter TAB (EmacsWiki)
(defun smart-tab ()
"This smart tab is minibuffer compliant: it acts as usual in
the minibuffer. Else, if mark is active, indents region. Else if
point is at the end of a symbol, expands it. Else indents the
current line."
(interactive)
(if (minibufferp)
(unless (minibuffer-complete)
(hippie-expand nil))
(if mark-active
(indent-region (region-beginning)
(region-end))
(if (looking-at "\\_>")
(hippie-expand nil)
(indent-for-tab-command)))))
(global-set-key (kbd "TAB") 'smart-tab)
You could have hippie expand settings as follows:
;;settings for hippie-expand
(setq hippie-expand-try-functions-list
'(try-complete-lisp-symbol
try-complete-lisp-symbol-partially
try-expand-dabbrev
try-expand-dabbrev-from-kill
try-expand-dabbrev-all-buffers
try-expand-line
try-complete-file-name-partially
try-complete-file-name))
C-M-i; no customization required.
I use:
(define-key function-key-map [(control tab)] [?\M-\t])
I use M-. and M-/ for the 2 completion modes - hippie-expand and the standard emacs one.
Put this in your .emacs to make Windows give Emacs the use of M-TAB:
(when (fboundp 'w32-register-hot-key) (w32-register-hot-key [M-tab]))