I would like to make a snippet of code that only executes if the point is on a different line in a particular buffer since the last user command. The only way I could think to do it was using the post-command-hook:

(setq my-prev-line 0)

(defun my-install-move-hook (buf)
  (with-current-buffer buf
    (add-hook 'post-command-hook 'my-move-hook nil t)))

(defun my-move-hook ()
  (when (and my-prev-line (= my-prev-line (line-number-at-pos)))
    (message "The point moved from line number %d to %d" my-prev-line (line-number-at-pos))
    (setq my-prev-line (line-number-at-pos))))

That seems like overkill, since post-command-hook triggers after every command, not just user-invoked commands. Would it be better if the line number was stored during pre-command-hook?

  • 2
    There is only rarely any distinction between "commands" and "user-invoked commands", so that comment sounds a little odd. Which are the 'not-user-invoked' commands you are wishing to avoid?
    – phils
    May 17 at 10:11
  • A user-invoked command is a command that is directly invoked by the user typing a keyboard shortcut for the command or running M-x <name of command>. For example, suppose that I have commands foo and baz, and baz calls foo 3 times. If the user invokes either foo or baz directly, there has been only one user-invoked command, but the post-command-hook would be called either once or four times (assuming that foo did something trivial).
    – shankar2k
    May 17 at 13:02
  • 1
    If bar calls (foo ...) that's not a separate command. A typical command might cause vast numbers of function calls, but it's still just one time around the command loop, and post-command-hook runs at the end of the command loop. Calling a function is not the same thing as invoking a command (even when the function being called could itself be invoked as a command).
    – phils
    May 17 at 14:01
  • 1
    Calculating line numbers is expensive. Why don't you place a marker at the beginning of the line in the pre-command hook and check if it's on the same line as the point in the post-command hook? May 17 at 14:59
  • 1
    @shankar2k, because finding the position of the start of the line is fast (at worst, Emacs steps one character at a time until the beginning of the line). Calculating the liner number requires Emacs to search through the entire text from the beginning to the point. May 18 at 12:49

You say: "post-command-hook triggers after every command, not just user-invoked commands." And in comments you say that by "user-invoked" you really mean invoked interactively.

It's not true that post-command-hook triggers each time a function that is a command is invoked. It's triggered for such a function only when it is invoked interactively:

(defun foo () (interactive) (message "THIS: %s, FOO" this-command) (bar))
(defun bar () (interactive) (message "THIS: %s, BAR" this-command))
(defun toto () 
  (message "TOTO, THIS: %s" this-command)
  (when (equal this-command 'foo)
    (message "TOTO, FOO was invoked")))

(add-hook 'post-command-hook 'toto)

This is what *Messages* shows, when you evaluate (foo), that is, you do not invoke foo interactively (i.e., as a command):

THIS: eval-last-sexp, FOO
THIS: eval-last-sexp, BAR
"THIS: eval-last-sexp, BAR"
TOTO, THIS: eval-last-sexp

And this is what *Messages* shows when you do M-x foo, that is, you invoke foo interactively (i.e., as a command):

TOTO, THIS: execute-extended-command
TOTO, THIS: self-insert-command [3 times]
THIS: foo, FOO
THIS: foo, BAR
TOTO, FOO was invoked

You can put conditional code in your hook function, to do whatever you want when foo is invoked as a command. That's what the test (equal this-command 'foo) does above. You can see that toto prints TOTO, FOO was invoked only when foo is invoked as a command (e.g. M-x).

Alternatively, depending on what you really want, use function called-interactively, to test whether foo has been invoked as a command.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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