16

From time to time I observe unexpected behavior when editing text. My first recourse is usually to use C-h k to find out what functions are being called by a given key sequence. However, sometimes the documentation is at odds with the observed behavior. In these cases, I usually assume that some other package has hooked into that function or key sequence and is modifying its behavior.

How can I find out which functions are hooking into my key sequence?

One example that I encountered recently was that I pressed the " key and quotation marks were inserted at the beginning and end of the active region. I had a suspicion that this was not the default Emacs behavior, so I used C-h k " to find out what function was actually being called.

The describe-key documentation told me that the function self-insert-command was being called, which is a built-in function. To make a long story short, after some amount of trial and error, I was able to determine that the behavior was caused by electric-pair-mode. In the future, is there a faster way to arrive at this conclusion than to turn off suspected packages one at a time until finding the culprit?

Drew
  • 75,699
  • 9
  • 109
  • 225
nispio
  • 8,175
  • 2
  • 35
  • 73
  • Is it possible that the `electric-pair-mode` was activated in only some major modes? Do you still see `self-insert-command` for `"` when you do `C-h k` while `electric-pair-mode` is active? – Kaushal Modi Sep 24 '14 at 20:16
  • @kaushalmodi: It turns out that `electric-pair-mode` works by hooking into `post-self-insert-hook`, and not by changing the keybinding. – nispio Sep 24 '14 at 20:59
  • `C-h k` tells you exactly what happens for a keypress. If you look at the documentation for `self-insert-command`, it very clearly says `post-self-insert-hook` is run after the command finishes. – shosti Sep 24 '14 at 21:01
  • @shosti: In this simple example, yes. But what if what if an extension uses `after-change-functions` like Jordon mentions in his answer? The documentation for a function probably won't specifically mention that hook, will it? – nispio Sep 24 '14 at 21:05
  • Sorry, I should have been more specific. `C-h k` + the standard hooks == the complete behavior (more or less). Of course sometimes that leaves a lot of possibilities, but it's still a lot more transparent than any other complex software system I know of. – shosti Sep 24 '14 at 21:17
  • @shosti: My question is not meant to be a complaint. Until Jordon's answer I did not even know that there was a list of "standard hooks" that I could check, hence my question. – nispio Sep 24 '14 at 22:35
  • This is a *failing in the design* of `electric-pair-mode`, IMO. A user should **not** need to investigate `post-self-insert-hook` - which was added recently (Emacs 24), BTW, in order to fudge stuff for `electric-pair-mode` (IIRC). `C-h k` should tell users what they need to know about what a key does, without making them chase around Robinson's barn to follow things like `post-self-insert-hook`. (Just one opinion.) – Drew Sep 25 '14 at 02:59
  • @Drew: I can't say I agree. The alternative is to override all the "pairing" keys, but what if another mode also wants to do something special with those keys, should it override them as well? Hooks are more modular and scale nicely to multiple minor modes. – shosti Sep 25 '14 at 18:25
  • @shosti: I have nothing against hooks. And yes, the same problem exists for things like `delete-selection-mode`. I don't have a good solution, I'm afraid. And it is at least good that the doc for `self-insert-command` mentions the hook, so users can track this down. Still, it is a far cry from `C-h k` telling you just what the behavior is, directly. – Drew Sep 25 '14 at 19:45

2 Answers2

14

There is no easy way to know exactly what a single key press will do.

If you see additional behavior always check the common hooks. See the list here: http://www.gnu.org/software/emacs/manual/html_node/elisp/Standard-Hooks.html

In most cases the important ones are:

  • after-change-functions
  • before-change-functions
  • first-change-hook
  • post-command-hook
  • pre-command-hook
  • post-self-insert-hook

You'll need to inspect those hooks and look into the functions they contain to see which one is modifying your behavior.

If the functions in these hooks don't fully describe the observed behavior, check the functions for advice which will show up in their documentation from describe-function.


Edit: I've written some functions to help describe a hook better than going through the functions one by one: https://gist.github.com/jordonbiondo/bad03e44bb053db0f1eb You can use describe-hook defined there like the other describe functions. Here is a sample of it's output:

And here's all the code, in case the gist disappears:

(defun guess-all-hooks ()
  "Return a list of all variables that are probably hook lists."
  (let ((syms '()))
    (mapatoms
     (lambda (sym)
       (if (ignore-errors (symbol-value sym))
           (let ((name (symbol-name sym)))
             (when (string-match "-\\(hook[s]?\\|functions\\)$" name)
               (push sym syms))))))
    syms))

(defun face-it (str face)
  "Apply FACE to STR and return."
  (propertize str 'face face))

(defun describe-hook (hook)
  "Display documentation about a hook variable and the
functions it contains."
  (interactive
   (list (completing-read
          "Hook: " (mapcar (lambda (x) (cons x nil)) (guess-all-hooks)))))
  (let* ((sym (intern hook))
         (sym-doc (documentation-property sym 'variable-documentation))
         (hook-docs (mapcar
                     (lambda (func)
                       (cons func (ignore-errors (documentation func))))
                     (symbol-value sym))))
    (switch-to-buffer
     (with-current-buffer (get-buffer-create "*describe-hook*")
       (let ((inhibit-read-only t))
         (delete-region (point-min) (point-max))
         (insert (face-it "Hook: " 'font-lock-constant-face) "\n\n")
         (insert (face-it (concat "`" hook "'") 'font-lock-variable-name-face))
         (replace-string "\n" "\n\t" nil
                         (point)
                         (save-excursion
                           (insert "\n" sym-doc "\n\n")
                           (1- (point))))
         (goto-char (point-max))
         (insert (face-it "Hook Functions: " 'font-lock-constant-face) "\n\n")
         (dolist (hd hook-docs)
           (insert (face-it (concat "`" (symbol-name (car hd)) "'")
                            'font-lock-function-name-face)
                   ": \n\t")
           (replace-string "\n" "\n\t" nil
                           (point)
                           (save-excursion
                             (insert (or (cdr hd) "No Documentation") "\n\n")
                             (1- (point))))
           (goto-char (point-max))))
       (help-mode)
       (help-make-xrefs)
       (read-only-mode t)
       (setq truncate-lines nil)
       (current-buffer)))))
Jordon Biondo
  • 12,332
  • 2
  • 41
  • 62
  • Does that mean that when a function gets advised, the documentation is automatically updated to reflect the change? – nispio Sep 24 '14 at 20:44
  • I do no know if the actual properties get updated, but the value returned by `documentation` is updated to reflect. – Jordon Biondo Sep 24 '14 at 20:59
  • 1
    @nispio yes, it is. – Malabarba Sep 24 '14 at 21:33
  • 1
    The code/function in https://gist.github.com/jordonbiondo/bad03e44bb053db0f1eb could, and in my opinion, should, be included in the answer. I think a SE answer has a limit of 30,000 characters. – Faheem Mitha Sep 25 '14 at 13:39
5

Perhaps not a full answer to your question, but the package helm-descbinds helps you search all the defined keyboard bindings from the ascii representation of the shortcut. For every hit, it shows you the interactive function associated with the keyboard shortcut, and you can ask helm-descbinds to describe it, or execute it directly from the search results.

enter image description here

Here is the package description from the GitHub site:

Helm Descbinds provides an interface to emacs’ describe-bindings making the currently active key bindings interactively searchable with helm.

Additionally you have the following actions

  • Execute the command
  • Describe the command
  • Find the command

And C-z will give you a persistent description of the current command.

Amelio Vazquez-Reina
  • 5,157
  • 4
  • 32
  • 47
  • 2
    This is great. I will definitely bind this to `C-h b`. My only wish is that I could jump to the item on the list by entering the actual key sequence instead of typing out `C` `-` `c` `_` `C` `-` `p` – nispio Sep 26 '14 at 00:20