24 July 2007

my dot emacs: Shift to Control

I can still remember my left pinky hurting badly, from pressing the Control key to invoke commands in Emacs. But then I swapped Shift & Control. And I've been happy ever since. Shift is in a perfect location (not too far below like Control) and is on both sides of the keyboard -- this even on the worst keyboards that I had to endure while in college.

Now, some things break when you do this. Control-Space sets the mark (and still does), but Shift-Space doesn't -- it simply inserts a space. Control-G can break out of infinite loops in Emacs Lisp -- Shift-g can't do that (unless you modify the quit_char in src/keyboard.c before building Emacs). Also, typing upcase or capitilized characters are a bit harder -- especially because I move my entire hand off the keyboard to hit the Control key -- no more use of the pinky in unnatural positions! And when you cut-n-paste, upper case characters run commands!! (Although, I haven't needed it, this shouldn't be too hard to overcome -- you can setup things so that a keypress swaps Control & Shift, and then pastes the contents of the clipboard, and the re-swaps them again.)

Despite the problems, this has been worth it for me. And here is the code for all this. I think this is all the code -- I haven't really looked at it since I wrote it about 6 years ago. This works on both Windows and Linux (I think, this will really work everywhere...):

;;; control becomes shift and vice-versa. when this happens certain
;;; things are broken: c-space sets mark, however shift-space doesn't
;;; anymore.  for esc, use alt (press this with the thumb, which
;;; doesn't do *anything* except hit the damn space bar) plus the key.
;;; expriment: 04:29PM Friday November 23 2001.  hopefully, things
;;; will be much easier this way.
;;; to do: change esc-c-f to esc-F.  also need to be careful about
;;; esc-ctrl- like esc-ctrl-. and esc-ctrl-backspace. how to
;;; fix these.  also since i usually type a lowercase word and want to
;;; upcase or capatilize it afterwards, make those commands default to the
;;; previous word.

;;; Another idea:  make keyboard-translate-table buffer local so that
;;; in certain modes, like gnus, we can operate normally (or maybe
;;; change it during an entry and exit hook.

(defun keyboard-translate-swap (a b)
  (keyboard-translate a b)
  (keyboard-translate b a))

(defvar previous-keyboard-translate-table nil)

(defun normalize-keyboard ()
  (interactive)
  (setq previous-keyboard-translate-table keyboard-translate-table
        keyboard-translate-table nil))

(defun revert-keyboard ()
  (interactive)
  (setq keyboard-translate-table previous-keyboard-translate-table))

;;; Emacs Manual, "Translating Input Events"
(setq keyboard-translate-table
      (make-char-table 'keyboard-translate-table nil))

;;; Swap Control- and uppercase letters.
(let ((ctrl 1) (upcase-beg (1- ?A)) (letters 26))
  (while (<= ctrl letters)
    (keyboard-translate-swap ctrl (+ upcase-beg ctrl))
    (setq ctrl (1+ ctrl))))

And this piece of code quickly upcases or capitalizes the previous word that you just typed. I have it bound to F6:

(defvar my-correct-case-last-arg nil)

(defun my-correct-case (arg)
  (interactive "p")
  (let ((fn (cond ((eq last-command 'my-correct-case)
                   (setq arg my-correct-case-last-arg)
                   'capitalize-word)
                  (t (setq my-correct-case-last-arg arg)
                     'upcase-word))))
    (save-excursion
      (funcall fn (- arg)))))

These days I run Emacs via Putty from a Windows machine. This is how I cut-n-paste in Putty -- when you right click on the terminal, Putty sends the contents of the clipboard to the terminal. There are 2 parts to this: the Emacs part and the shell part. First, I have to suspend Emacs and go to the shell, where is type the alias c:

alias c='echo Hit Ctrl-C to end; cat > ~/x'

Next, back in Emacs, I hit F10 -- this function is bound to that key:

(defun my-paste ()
  (interactive)
  (if (not (file-exists-p "~/x"))
      (message "~/x doesn't exist")
    (insert-file-contents "~/x")
    (message "Pasted contents of ~/x")))