views:

105

answers:

2

I'm attempting my first modification of Emacs. I recorded a little keyboard macro and had Emacs spit it out as elisp, resulting in:

(setq add-docstring
    "\C-rdef\C-n\C-a\C-m\C-p\C-i\C-u6\"\C-u3\C-b")
(global-set-key "\C-c\C-d" 'add-docstring)

Searching the Emacs reference, though, revealed that C-c C-d is already bound in diff mode. I don't plan on using diff mode, but the future is unknowable and I'd like to not lay a trap for myself. So I'd like this keybinding to only operate in python mode, where it tries to help me add docstrings.

In my /usr/share/emacs/23.whatever/list/progmodes, I found python.elc and python.el.gz. I unzipped python.el.gz and got a readable version of the elisp file. Now, though, the documentation becomes opaque to me.

How can I add my key binding to the python mode, instead of globally?

Is it possible, for bonus points, to apply the changes to python mode without restarting emacs or closing open files? It's the self-modifying editor, I figure there's a good chance that it's possible.

+1  A: 

Firstly, do not mess with python.el. What you want to do is to create your own custom binding for python mode. This is usually done in your .emacs file in your home directory.

In that file add something like the following (I have not tested this -- so there may be some error in syntax and I do not use python myself)

(add-hook 'python-mode-hook
     '(lambda () (define-key python-mode-map "\C-c\C-d" 'add-doc-string)))

This is using the hook mechanism. It is a function that is called each time you invoke python mode. This function only binds C-c C-d to your add-doc-string function.

This answer is very brief. Read about the .emacs file, customizations and hooks in the emacs documentation.

Andrew Stein
I came to SO with my question because I'd already tried grepping the documentation and things weren't making sense.
Sean M
+6  A: 

It turns out, C-c C-d is already bound in python-mode (to 'python-pdbtrack-toggle-stack-tracking), so you may want to revisit your choice of key binding.

Note: if you just want to cut/paste a solution, jump to the end of the answer. Read on for an explanation of how to get there - in case you want to do it again.

The macro is a good start, but what you have won't quite work. To get something you can bind to a key, try M-x insert-kbd-macro for that macro, and you'll get:

(fset 'add-docstring
   (lambda (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item (quote ("def ...unprintable characters...6\"3" 0 "%d")) arg)))

(hm.... non-printable characters, I can't cut/paste into SO, but you can do it yourself to get the right thing). With a little munging, what you get is equivalent to this:

(fset 'add-docstring
      (lambda (&optional arg)
        "Keyboard macro."
        (interactive "p")
        (kmacro-exec-ring-item `(,(kbd "C-r def C-n C-a C-m C-p C-i C-u 6 \" C-u 3 C-b") 0 "%d")
                               arg)))

That's the first step. With the above, you can do M-x add-docstring and get the behavior you want.

The next step is what you asked - how to bind to keys locally. The documentation for keybindings begins here, and of interest to you is the Local Keymaps section, which leads to the following:

(add-hook 'python-mode-hook
          (lambda () (define-key python-mode-map (kbd "C-c C-d") 'add-docstring)))

This sets up an anonymous function to be called when python-mode is turned on, and that function does one thing - it sets up the key binding you want in the keymap specifically for python-mode.

If you read the Keymaps section closely, you'll see that Emacs follows the convention that only users should bind commads to C-c a, where a is any lower or upper-case letter (e.g. C-c d C-c T C-c p are all available), and packages constrain mode-specific bindings to C-c %, where % is any punctuation character or control key (e.g. C-c C-c C-c [ C-c C-z).

So, if you change your binding to C-c d, then you're pretty much guaranteed not to conflict with any packages out there. The python.el shipped with Emacs does follow these conventions, as do most (all?) packages shipped with Emacs.

You'll notice that I use kbd to read in the key sequences. It is portable and I find it a lot easier to read.

There are further things you could do to clean this up:

  1. put the customizations in a named function
  2. rewrite the macro in elisp
  3. use eval-after-load instead of the hook (see this question)

Here's what I'd do for #1, which gives you a handy place to put other customizations:

(add-hook 'python-mode-hook 'my-python-customizations)
(defun my-python-customizations ()
  "set up my personal customizations for python mode"
  ;; put other customizations in here
  (define-key python-mode-map (kbd "C-c C-d") 'add-docstring))
(defun add-docstring (&optional arg)
  "Keyboard macro."
  (interactive "p")
  (kmacro-exec-ring-item `(,(kbd "C-r def C-n C-a C-m C-p C-i C-u 6 \" C-u 3 C-b") 0 "%d")
                         arg))

Using a named function is a little cleaner in that you can later do (remove-hook 'python-mode-hook 'my-python-customizations) if you so desire. Also, if you look at the value for the hook (C-h v python-mode-hook RET), it's obvious what gets called (the anonymous function is longer and more difficult to read).

For the bonus points, after you pasted the code into your .emacs do M-x eval-region, which will tell Emacs to evaluate the code in the region. To see the changes in your existing python buffers, you'd just have to open a new python file/buffer, which would trigger the keybinding change - which is common to all python buffers.

Happy hacking.

Trey Jackson
Good job answering the question I _meant_ to ask instead of what I actually asked. This, I think, points me in the right direction.
Sean M