Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

counsel-M-x should not set last-command #891

Closed
Wilfred opened this issue Feb 17, 2017 · 6 comments
Closed

counsel-M-x should not set last-command #891

Wilfred opened this issue Feb 17, 2017 · 6 comments

Comments

@Wilfred
Copy link
Contributor

Wilfred commented Feb 17, 2017

I've noticed that using counsel-M-x sets last-command. This means that I can't do (if (eq this-command last-command) ...) to see if my command is being called repeatedly.

@abo-abo
Copy link
Owner

abo-abo commented Feb 17, 2017

counsel-M-x is supposed to mirror execute-extended-command, which sets last-command for the same reason that you mention.

@Wilfred
Copy link
Contributor Author

Wilfred commented Feb 17, 2017

That's not the behaviour I'm seeing:

(global-set-key (kbd "M-x") #'counsel-M-x)
(global-set-key (kbd "C-c M-x") #'execute-extended-command)

(defun wh/report-last-command ()
  (interactive)
  (message "last-command: %s" last-command))

With ivy-mode disabled, C-c M-x wh/report-last-command repeatedly shows the messages:

last-command: other-window
last-command: wh/report-last-command
last-command: wh/report-last-command

M-x wh/report-last-command repeatedly shows the messages:

last-command: self-insert-command
last-command: counsel-M-x
last-command: counsel-M-x

The first invocation looks like counsel-M-x has set last-command based on commands called in the minibuffer, as self-insert-command is because I typed the name of the command. If I move around in the counsel minibuffer (M-x C-n C-p) to select the wh/report-last-command function I get:

last-command: ivy-previous-line

@abo-abo
Copy link
Owner

abo-abo commented Feb 17, 2017

I don't why results that you show happen. But I know that this bit is needed:

(let ((prefix-arg current-prefix-arg))
  (setq real-this-command
        (setq this-command (intern cmd)))
  (command-execute (intern cmd) 'record))

Here's my experience: counsel-M-x -> forward-char -> eval-expression (last-command) shows forward-char.

@Wilfred
Copy link
Contributor Author

Wilfred commented Feb 17, 2017

Yep, I'm seeing the same as you. eval-expression shows the correct value of last-command, but the command being invoked (wh/report-last-command in this case) does not.

@abo-abo
Copy link
Owner

abo-abo commented Feb 17, 2017

The repeat functionality also works, and I've had no complaints about counsel-M-x for a long while.

I don't know why your function doesn't work. Patches for a fix are welcome, but let's make sure the standard functionality still works.

@Wilfred
Copy link
Contributor Author

Wilfred commented Feb 17, 2017

I suspect the problem is that we're setting this-command in a callback. When counsel-M-x returns, Emacs does (setq last-command this-command) we end up with last-command being set to counsel-M-x.

Here's one possible patch:

(defun counsel-M-x (&optional initial-input)
  "Ivy version of `execute-extended-command'.
Optional INITIAL-INPUT is the initial input in the minibuffer."
  (interactive)
  (unless initial-input
    (setq initial-input (cdr (assoc this-command
                                    ivy-initial-inputs-alist))))
  (let* ((cands obarray)
         (pred 'commandp)
         (sort t))
    (when (require 'smex nil 'noerror)
      (unless smex-initialized-p
        (smex-initialize))
      (smex-detect-new-commands)
      (smex-update)
      (setq cands smex-ido-cache)
      (setq pred nil)
      (setq sort nil))
    ;; Ensure that `counsel-M-x' is never stored in `last-command',
    ;; for consistency with `execute-extended-command'. The :action
    ;; is invoked after `counsel-M-x' returns, so that isn't sufficient.
    (setq this-command last-command)
    (setq real-this-command real-last-command)

    (ivy-read (counsel--M-x-prompt) cands
              :predicate pred
              :require-match t
              :history 'extended-command-history
              :action
              (lambda (cmd)
                (when (featurep 'smex)
                  (smex-rank (intern cmd)))
                (setq this-command (intern cmd))
                (setq real-this-command (intern cmd))
                (let ((prefix-arg current-prefix-arg))
                  (command-execute (intern cmd) 'record)))
              :sort sort
              :keymap counsel-describe-map
              :initial-input initial-input
              :caller 'counsel-M-x)))

Another option would be to save last-command and real-last-command in counsel-M-x and also set those in the :action callback. What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants