Skip to main content
Notice removed Draw attention by shackra
Bounty Ended with clemera's answer chosen by shackra
Notice added Draw attention by shackra
Bounty Started worth 100 reputation by shackra
added 53 characters in body; edited tags
Source Link
Drew
  • 79.1k
  • 10
  • 123
  • 257
Source Link
shackra
  • 2.8k
  • 20
  • 51

How to force the user to comply with a command?

First, here is the full source code of my package.

Requirement

Currently I'm working on a package that provides a feature which upon the user invoking several times in a row certain command in a short amount of time, an alternative command is invoke instead. The assumption is made on the basis that the user has forgotten that the alternative command exist and that it is better/more efficient to achieve what he wants to achieve than the command he is using now.

Example use case

many invocations of next-line or previous-line through its bindings (either , C-n or , C-p), instead of letting the user keep invoking those two commands many more times, my package comes in and calls avy-goto-line (because that's how the user previously configured it to work).

The problem

I want to force the user to comply with whatever input a command request, right now if the user fall asleep on any binding for next-line, the alternative command goto-line never executes, or at least that's how it looks. If the alternative command were avy-goto-line it will throw an wrong-type-argument rendering my feature useless for next invocations if I don't ignore-errors.

I tried something like this:

(let* ((exit nil))
  (while (not exit)
    (setf exit (ignore-errors (avy-goto-line)))))
    

And it works, it does what I want to achieve. But for functions like goto-line or even avy-goto-word-0 Emacs gets freeze and the mini-buffer never popups asking something to the user.

the code

Here is the relevant parts of my code:

(setf schrute-shortcuts-commands '((goto-char  (left-char right-char))
                                   (goto-line  (next-line previous-line))))

(defun schrute--run-command ()
  "Helper that will run an alternative-command."
  (let* ((alternative-command)
         (command-list))
    (dolist (elem schrute-shortcuts-commands)
      (setf alternative-command (car elem))
      (setf command-list (cadr elem))
      (when (or (member this-command command-list)
               (eq this-command command-list))
        (funcall-interactively alternative-command)))))
        
(defun schrute-check-last-command ()
  "Check what command was used last time.

It also check the time between the last two invocations of the
same command and use the alternative command instead."
  (when (eq this-command last-command)
    (if (member this-command schrute--interesting-commands)
        (let* ((time-passed (float-time (time-subtract (current-time) schrute--time-last-command))))
          (if (<= time-passed 1.0)
              (setf schrute--times-last-command (1+ schrute--times-last-command)))
          (setf schrute--time-last-command (current-time)))))
  (when (> schrute--times-last-command 2)
    (setf schrute--times-last-command 0)
    ;; Call the alternative command for `this-command'
    (ignore-errors (schrute--run-command))))