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?

9
  • 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? Sep 24, 2014 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, 2014 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, 2014 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, 2014 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, 2014 at 21:17

2 Answers 2

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)))))
4
  • Does that mean that when a function gets advised, the documentation is automatically updated to reflect the change?
    – nispio
    Sep 24, 2014 at 20:44
  • I do no know if the actual properties get updated, but the value returned by documentation is updated to reflect. Sep 24, 2014 at 20:59
  • 1
    @nispio yes, it is.
    – Malabarba
    Sep 24, 2014 at 21:33
  • 1
    The code/function in 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. Sep 25, 2014 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.

1
  • 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, 2014 at 0:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.