Swap the curly and the square braces in emacs

2

Currently in emacs I have to type SHIFT-[ and SHIFT-] to get { and } respectively. Typing [ and ] yields [ and ] respectively. I want to swap this behaviour in emacs, namely I want to have:

  • [ and ] will yield { and }
  • SHIFT-[ and SHIFT-] will yield [ and ]

This idea is inspired by https://tex.stackexchange.com/a/1985/412

BONUS: I want this behaviour to apply only for certain major modes.

Dror

Posted 2013-08-21T05:13:08.740

Reputation: 1 510

Answers

1

If you wanted this behavior throughout Emacs, it'd be trivial to achieve it by using key-translation-map, a keymap provided specifically for this purpose. Wanting to use it only in certain major modes complicates things somewhat. The best way I've found so far to do this is to define a minor mode which performs the translation according to a given keymap, and then add a hook to activate that minor mode along with each major mode to which you want it to apply.

Here's the minor mode definition. Drop this into a file swap-braces.el somewhere in your load path.

;;; swap-braces.el - a minor mode to swap square and curly braces

;;; Installation: 

;; Drop it in your load path and (require 'swap-braces).
;; Possibly also (add-hook 'foo-mode-hook 'swap-braces-mode).

;;; Commentary:

;; The swap function explicitly instructs Linum to schedule an update,
;; if Linum mode is enabled; otherwise, Linum does not automatically 
;; update while Swap-Braces mode is enabled.
;; This seems like it should not be necessary, and I'm uncertain whether 
;; it has greater implications regarding interaction between this mode 
;; and others. 
;; (That is, it makes me worry that something about how I've implemented 
;; this makes it screw up other modes using post-command-hook. Caveat user!)

;;; Author

;; Aaron Miller (me@aaron-miller.me)

(setq swap-braces-map (make-sparse-keymap))

(define-key swap-braces-map (kbd "{") "[")
(define-key swap-braces-map (kbd "}") "]")
(define-key swap-braces-map (kbd "[") "{")
(define-key swap-braces-map (kbd "]") "}")

(define-minor-mode swap-braces-mode
  "When active, swap square braces and curly braces, so that e.g. pressing [
elicits '{', and S-[ elicits '['.

The keymap `swap-braces-map' defines this mode's behavior. It is strongly
recommended that you do not define keys in this map which are not self-
inserting characters; the resulting behavior is indeterminate, but almost
certainly not what you intended."
  :init-value nil
  :lighter " [{"
  (if swap-braces-mode
      (add-hook 'post-command-hook
                'swap-braces-mode-swap)
    (remove-hook 'post-command-hook
                 'swap-braces-mode-swap)))

(defun swap-braces-mode-swap ()
  ;; this worries me
  (if (and (boundp 'linum-mode)
           (not (eq nil linum-mode)))
      (linum-schedule))
  (when (and (eq (char-before) last-command-event)
             (not (window-minibuffer-p (selected-window))))
    (let* ((translation (lookup-key swap-braces-map (string (char-before)))))
      (if (stringp translation)
          (progn
            (backward-delete-char 1)
            (insert translation))))))

(provide 'swap-braces)

It's not perfect; note the caveats regarding linum-mode, and what this odd misbehavior may imply about this mode's effects on others which use post-command-hook. I use several such modes, and haven't seen any problems (other than linum not updating) during testing, but your mileage may vary, so be aware. (Oddly enough, using post-self-insert-hook instead made no difference here; I've settled on post-command-hook because post-self-insert-hook is apparently new with Emacs 24, and it offers no difference in behavior worth abandoning backwards compatibility.)

Then, somewhere in your initialization code, (require 'swap-braces); from now on, you can activate the swapping behavior with M-x swap-braces-mode.

Finally, to activate the minor mode along with a major mode, add it to the major mode's load hook. If the major mode you're using is named 'foo-mode', then you'd add the following to your initialization code:

(add-hook 'foo-mode-hook 'swap-braces-mode)

Adjust to taste; in your case, it'll probably be called latex-mode-hook, but check the mode's documentation to make certain.

Edited on 2013-10-09 to apply translation only when the selected window isn't the minibuffer; see comments.

Aaron Miller

Posted 2013-08-21T05:13:08.740

Reputation: 8 849

Can you explain what is the role of linum-mode? – Dror – 2013-08-21T19:16:11.917

@Dror It's a (potentially global) minor mode, included in the GNU Emacs standard library, which provides line numbers in the left-hand fringe of windows where it's active. (Linum on EmacsWiki; screenshot from the developer's (rather minimal) site; source.)

– Aaron Miller – 2013-08-21T19:17:24.833

Seems like this solution messes up the behavior of those keys. For example, when doing a regexp search, things are not stable, or when inserting something to the minibuffer in general... I did not expect it to be such a big problem... – Dror – 2013-10-09T11:54:06.670

@Dror Translating keystrokes isn't a big problem; doing it only in certain contexts gets tricky fast. I've just edited swap-braces-mode-swap so as not to translate strokes in the minibuffer; that should give you the behavior you're looking for. I would further strongly advise learning some Emacs Lisp; what I've posted here is more of a starting point than a complete solution, which means you'll probably want to hack it to taste. The language isn't hard to learn, and how forbidding and alien you find Emacs is inversely proportional to how well you know Emacs Lisp. – Aaron Miller – 2013-10-09T15:18:32.557

1

Lets see...

M-x describe-key [
\\    [ runs the command self-insert-command ...
M-x describe-key
\\    { runs the command self-insert-command ...

It looks like we can just swap them naively by adding

(global-set-key (kbd "{") (lambda () (interactive) (insert "[")))

to .emacs. For specific modes, add things like that to the appropriate mode-hook instead of setting them globally. So,

(add-hook 'some-mode-hook
          (lambda ()
             (define-key some-mode-map (kbd "{") (lambda () (interactive) (insert "[")))
             ...))

I get the feeling this isn't really the best solution though. If you use things like autopairs or paredit with those brace styles, Odd Things Will Happen™©.

Inaimathi

Posted 2013-08-21T05:13:08.740

Reputation: 350

Sorry for the downvote, but "naive" understates the depth of the problem here. See my answer, to be posted momentarily. – Aaron Miller – 2013-08-21T14:42:07.920

On the other hand, key-translation-map doesn't look like it can be made mode-specific, so that's not an ideal solution either; un-downvoted. (It's still not a good idea to override key behavior this bluntly, though.) – Aaron Miller – 2013-08-21T14:49:58.060

At the moment I want to have this swap only in LaTeX-mode. I did not evaluate whether I want it in other modes or not. Thus, the bonus part is somehow important. I'll wait a bit longer. – Dror – 2013-08-21T14:56:01.813

@Dror I've just replaced my answer with a minor mode that should do what you want. – Aaron Miller – 2013-08-21T16:25:06.350

This solution is light and easy to implement. However, due to @AaronMiller comment it seems like this solution can (I fail to understand how) cause problems. Aaron's solution works as well. – Dror – 2013-08-21T19:53:37.950

@Dror The lightweight solution unconditionally redefines the behavior of the key, which causes problems in cases where, for example, a major mode has already bound some programming-language-specific behavior to that key -- the mode's binding is replaced, not augmented. Using a hook leaves the key's existing binding untouched, and therefore avoids this problem. – Aaron Miller – 2013-08-21T20:00:56.877

@AaronMiller - Correct, and don't apologize for downvoting. I tend to prefer simple solutions where possible because, while they might be stupid, they're at least obviously stupid. I would appreciate it if you didn't randomly add trailing spaces to my answers though. – Inaimathi – 2013-08-21T20:51:40.247

@Inaimathi The argument is not without merit, but where the simple solution is also obviously broken in common cases, it does seem somewhat lacking. Sorry about the trailing space, but Super User wouldn't let me un-downvote unless the answer was edited, so... – Aaron Miller – 2013-08-21T21:10:22.160