2

I would like to config emacs to run c-c c-k command after edit text happen and delay 3 secs. if edit happen within 3 secs, then no need to run the command. if edit happened then after 3 secs not edit happen, I need to run c-c c-k command.

How can I write my init script to get such result?

2 Answers 2

4

The function for doing this is called run-with-idle-timer. From the help for that function:

(run-with-idle-timer SECS REPEAT FUNCTION &rest ARGS)

Perform an action the next time Emacs is idle for SECS seconds. The action is to call FUNCTION with arguments ARGS.

See the full help with C-h f run-with-idle-timer, and the relevant manual section is (elisp) Idle Timers.

For your issue, you probably want something like:

(run-with-idle-timer 3 t #'my-function)

Where my-function is the name of the function called by C-c C-k. By default, C-c C-k isn't bound to anything, so I don't know what this does on your Emacs, it must be a custom configuration you have added.

Note that this will run my-function once every time Emacs is idle for more than 3 seconds. You only want it to run when the buffer has been edited. You can accomplish this by writing a wrapper that only calls my-function when buffer-edited-p returns t:

(defun my-idle-function ()
  (if (buffer-edited-p) (my-function))) 

And then something like this:

(run-with-idle-timer 3 t #'my-idle-function)
3
  • Awesome, exactly what I want. I am using skewer-mode to edit javascript. I wish this take effect only for skewer mode. and how can I know which function c-c c-k called?
    – lucky1928
    Commented May 1, 2022 at 15:38
  • With a buffer in skewer-mode, say C-h c C-c C-k, or (which is the same thing) M-x describe-key-briefly RET C-c C-k.
    – NickD
    Commented May 1, 2022 at 19:03
  • You accepted this answer; if it works OK for you in casual usage, great! But idle timers are not per-buffer. If you see problems (e.g. your skewer function runs on whatever buffer happens to be current) I could post a more complicated but more reliable approach as another answer. Commented May 2, 2022 at 16:03
2

As I commented on the accepted answer, it might work fine for you in casual use -- in which case, that's great, and you can ignore this answer!


What I think you need to do, for it to be most reliable, is unfortunately a little more complicated: Use an after-change hook to detect edits, and have that set an idle timer (passing it the buffer to use).

;; The basic plan here:
;;
;; 1. Use an after-change-hook, which is called only after the buffer
;; changes.
;;
;; 2. That sets a 3 second timer, after which the C-c C-k skewer
;; command is called.

(defvar-local my/idle-timer nil
  "Buffer-local variable for idle timer.")

(defun my/skewer-after-change-function (_beg _end _len)
  "A value for `after-change-functions' hook."
  ;; Cancel any existing idle timer
  (when (timerp my/idle-timer)
    (cancel-timer my/idle-timer))
  ;; Set a fresh timer for 3 seconds from now, to run once only. If it
  ;; expires, it will call `my/run-skewer-function', passing the value
  ;; of `current-buffer' in effect right now.
  (setq my/idle-timer
        (run-with-idle-timer 3   ;seconds
                             nil ;no repeat
                             #'my/run-skewer-function
                             (current-buffer))))

(defun my/run-skewer-function (buffer)
  "A function to be called from our idle timer.

Mainly it just checks the buffer is live, and makes it current before
calling the skewer command."
  (when (buffer-live-p buffer)
    (with-current-buffer buffer
      ;; Here call whatever command is bound to C-c C-k in skewer-mdoe
      (the-C-c-C-k-skewer-function)))) ;name TBD

(defun my/add-skewer-hook ()
  (add-hook 'after-change-functions
            #'my/skewer-after-change-function
            nil
            t)) ;LOCAL is true; important

(add-hook 'skewer-mode #'my/add-skewer-hook)

Note: I named the variable and functions using a my/ prefix so that hopefully they won't collide with anything else in your init file or Emacs.

3
  • Many thanks, when I try it, I got a error: Error running timer `my/run-skewer-function': (void-function buffer-livep)
    – lucky1928
    Commented May 2, 2022 at 17:23
  • Sorry about that typo, it should be buffer-live-p; I'll update the code in the answer. Commented May 2, 2022 at 17:58
  • By the way, the provenance of this example code is part of my Racket Mode package. Although that's tested, it addresses some other concerns, so I tried to simplify and adapt it for this answer. Commented May 2, 2022 at 18:04

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.