Smex is a M-x enhancement for Emacs. Built on top of IDO, it provides a convenient interface to your recently and most frequently used commands. And to all the other commands, too.

Extras: Limit commands to those relevant to the active major mode. Show frequently used commands that have no key bindings.


Via package.el, el-get or



Hyphen on Space

How to modify smex so that typing a space will insert a hyphen ‘-’ like in normal M-x?

      (defadvice smex (around space-inserts-hyphen activate compile)
        (let ((ido-cannot-complete-command 
               `(lambda ()
                  (if (string= " " (this-command-keys))
                      (insert ?-)
                    (funcall ,ido-cannot-complete-command)))))

If there is only one match it will be completed instead of inserting a hyphen (which may well be a desirable thing). To avoid that, fset ido-complete-space to that lambda form above, then fset it back after ad-do-it (flet doesn’t work for interactive functions).

See also ido-complete-space-or-hyphen.

Update less often

I don’t like to have smex update every time I run (auto-update is usually good enough, and is noticeably faster). But it’s annoying when I manually load a file and the new commands are not in smex. So I add a smex-update to after-load-functions.

    (defun smex-update-after-load (unused)
      (when (boundp 'smex-cache)
    (add-hook 'after-load-functions 'smex-update-after-load)

See also Update smex after function calls that are likely to define new commands

Using acronyms

You can see mnemonical commands (within completion) before the others with this code snippet:

(defadvice ido-set-matches-1 (around ido-smex-acronym-matches activate)
  "Filters ITEMS by setting acronynms first."
  (if (and (fboundp 'smex-already-running) (smex-already-running) (> (length ido-text) 1))

      ;; We use a hash table for the matches, <type> => <list of items>, where
      ;; <type> can be one of (e.g. `ido-text' is "ff"):
      ;; - strict: strict acronym match (i.e. "^f[^-]*-f[^-]*$");
      ;; - relaxed: for relaxed match (i.e. "^f[^-]*-f[^-]*");
      ;; - start: the text start with (i.e. "^ff.*");
      ;; - contains: the text contains (i.e. ".*ff.*");
      (let ((regex (concat "^" (mapconcat 'char-to-string ido-text "[^-]*-")))
            (matches (make-hash-table :test 'eq)))

        ;; Filtering
        (dolist (item items)
          (let ((key))
             ;; strict match
             ((string-match (concat regex "[^-]*$") item)
              (setq key 'strict))

             ;; relaxed match
             ((string-match regex item)
              (setq key 'relaxed))

             ;; text that start with ido-text
             ((string-match (concat "^" ido-text) item)
              (setq key 'start))

             ;; text that contains ido-text
             ((string-match ido-text item)
              (setq key 'contains)))

            (when key
              ;; We have a winner! Update its list.
              (let ((list (gethash key matches ())))
                (puthash key (push item list) matches)))))

        ;; Finally, we can order and return the results
        (setq ad-return-value (append (gethash 'strict matches)
                                      (gethash 'relaxed matches)
                                      (gethash 'start matches)
                                      (gethash 'contains matches))))

    ;; ...else, run the original ido-set-matches-1

e.g. without this code the completion of ‘ffow’ shows:

{ ediff-show-registry | Buffer-menu-1-window | Buffer-menu-2-window ...


{ find-file-other-window | find-function-other-window | ediff-show-registry ...

See also smex with acronyms