3

I use an asterisk in my function names, so that to execute one of my own functions, I would begin by typing M-x *. Now I would like to save myself a keystroke and achieve the same by M-X. How do I do that?

I tried defining a keyboard macro with F3 M-x * F4, but that, for some reason, didn't work.

3

The key sequence M-x calls execute-extended-command. That command uses read-extended-command for reading from minibuffer.

In read-extended-command the c-function completing-read does the main job. But, the INITIAL-INPUT argument for that command is hard-coded to nil.

We should not temporarily advise completing-read since that function can be compiled-in. So we need to define our own my-read-extended-command which is a copy of completing-read up to the INITIAL-INPUT argument "*" instead of nil.

But, we can temporarily advice read-extended-command within execute-extended-command.

The following code shows how. It defines a command my-execute-extended-command that temporarily advices read-extended-command with the help of cl-setf and calls execute-extended-command interactively within the body of this cl-setf.

(require 'cl-lib)

(defun my-read-extended-command ()
  "Read command name to invoke in `my-execute-extended-command'.
This is almost a copy of `read-extended-command'.
The only difference is ?* as initial input to M-x."
  (minibuffer-with-setup-hook
      (lambda ()
        (add-hook 'post-self-insert-hook
                  (lambda ()
                    (setq execute-extended-command--last-typed
                              (minibuffer-contents)))
                  nil 'local)
    (set (make-local-variable 'minibuffer-default-add-function)
         (lambda ()
           ;; Get a command name at point in the original buffer
           ;; to propose it after M-n.
           (with-current-buffer (window-buffer (minibuffer-selected-window))
         (and (commandp (function-called-at-point))
              (format "%S" (function-called-at-point)))))))
    ;; Read a string, completing from and restricting to the set of
    ;; all defined commands.  Don't provide any initial input.
    ;; Save the command read on the extended-command history list.
    (completing-read
     (concat (cond
          ((eq current-prefix-arg '-) "- ")
          ((and (consp current-prefix-arg)
            (eq (car current-prefix-arg) 4)) "C-u ")
          ((and (consp current-prefix-arg)
            (integerp (car current-prefix-arg)))
           (format "%d " (car current-prefix-arg)))
          ((integerp current-prefix-arg)
           (format "%d " current-prefix-arg)))
         ;; This isn't strictly correct if `execute-extended-command'
         ;; is bound to anything else (e.g. [menu]).
         ;; It could use (key-description (this-single-command-keys)),
         ;; but actually a prompt other than "M-x" would be confusing,
         ;; because "M-x" is a well-known prompt to read a command
         ;; and it serves as a shorthand for "Extended command: ".
         "M-x ")
     (lambda (string pred action)
       (let ((pred
              (if (memq action '(nil t))
                  ;; Exclude obsolete commands from completions.
                  (lambda (sym)
                    (and (funcall pred sym)
                         (or (equal string (symbol-name sym))
                             (not (get sym 'byte-obsolete-info)))))
                pred)))
         (complete-with-action action obarray string pred)))
     #'commandp t
     "*"
     'extended-command-history)))

(defun my-execute-extended-command ()
  "Run `execute-extended-command' interactively.
Thereby, replace `read-extended-command' by `my-read-extended-command', which see."
  (interactive)
  (cl-letf (((symbol-function 'read-extended-command) #'my-read-extended-command))
    (call-interactively #'execute-extended-command)))

(bind-key "M-X" #'my-execute-extended-command)
2

The quick and dirty way is to pretend you pressed *.

(defun execute-my-extended-command (&rest args)
  "Read a command name to call, favoring commands that begin with `*'.

Like `execute-extended-command', but when called interactively, preload a
leading `*' into the minibuffer."
  (interactive)
  (if (interactive-p)
      (progn
        (setq unread-command-events (cons ?* unread-command-events))
        (call-interactively #'execute-extended-command))
    (funcall #'execute-extended-command args)))
(global-set-key [?\M-X] #'execute-my-extended-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.