diff --git a/README.markdown b/README.markdown index 6dbb6aa..220e7c1 100644 --- a/README.markdown +++ b/README.markdown @@ -49,6 +49,11 @@ next/previous match. `Enter` executes the selected command. `smex-major-mode-commands` runs Smex, limited to commands that are relevant to the active major mode. Try it with Dired or Magit. +### Acronyms +Set `smex-acronyms` to use acronyms instead of the whole command names. + +E.g.: `ff` instead of `find-file` or `ffow` instead of `find-file-other-window` + ### Command help `C-h f`, while Smex is active, runs `describe-function` on the currently selected command. diff --git a/smex.el b/smex.el index 6d4a72b..b76de6d 100644 --- a/smex.el +++ b/smex.el @@ -35,6 +35,13 @@ :group 'convenience :link '(emacs-library-link :tag "Lisp File" "smex.el")) +(defcustom smex-acronyms nil + "If non-nil, you can use acronyms instead of the whole command names. +E.g.: 'ff' instead of 'find-file', or 'ffow' instead of +'find-file-other-window." + :type 'boolean + :group 'smex) + (defcustom smex-auto-update t "If non-nil, `Smex' checks for new commands each time it is run. Turn it off for minor speed improvements on older systems." @@ -73,6 +80,7 @@ Set this to nil to disable fuzzy matching." (defvar smex-history) (defvar smex-command-count 0) (defvar smex-custom-action nil) +(defvar smex-activep nil) ;; t when we are into smex-completing-read() ;; Check if Smex is supported (when (equal (cons 1 1) @@ -134,7 +142,8 @@ Set this to nil to disable fuzzy matching." (ido-enable-prefix nil) (ido-enable-flex-matching smex-flex-matching) (ido-max-prospects 10) - (minibuffer-completion-table choices)) + (minibuffer-completion-table choices) + (smex-activep t)) (ido-completing-read (smex-prompt-with-prefix-arg) choices nil nil initial-input 'extended-command-history (car choices)))) @@ -479,5 +488,63 @@ sorted by frequency of use." (insert "\n ")))) (insert "\n)\n"))) +(defadvice ido-set-matches-1 (around ido-smex-acronym-matches activate) + "Filters ITEMS by setting acronynms first." + (if (and smex-acronyms smex-activep (> (length ido-text) 1)) + + ;; We use a hash table for the matches, => , where + ;; can be one of (e.g. `ido-text' is "ff"): + ;; - a-s: for acronym strict matching (i.e. "^f[^-]*-f[^-]*$"); + ;; - a-r: for acronym relaxed matching (i.e. "^f[^-]*-f[^-]*"); + ;; - prefix: the text start with (i.e. "^ff.*"); + ;; - substring: the text contains (i.e. ".*ff.*"); + ;; - flex: flexible matching (i.e. ".*f.*f.*"); + (let ((regex (concat "^" (mapconcat + (lambda (c) (regexp-quote (char-to-string c))) + ido-text "[^-]*-"))) + (flex-regex (mapconcat + (lambda (c) (regexp-quote (char-to-string c))) + ido-text ".*")) + (matches (make-hash-table :test 'eq))) + + ;; Filtering + (dolist (item items) + (let ((key)) + (cond + ;; strict match + ((string-match (concat regex "[^-]*$") item) + (setq key 'a-s)) + + ;; acronym matching + ((string-match regex item) + (setq key 'a-r)) + + ;; prefix matching + ((string-match (concat "^" ido-text) item) + (setq key 'prefix)) + + ;; substring matching + ((and (not ido-enable-prefix) (string-match ido-text item)) + (setq key 'substring)) + + ;; flexible matching + ((and ido-enable-flex-matching (string-match flex-regex item)) + (setq key 'flex))) + + (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 'a-s matches) + (gethash 'a-r matches) + (gethash 'prefix matches) + (gethash 'substring matches) + (gethash 'flex matches)))) + + ;; ...else, run the original ido-set-matches-1 + ad-do-it)) + (provide 'smex) ;;; smex.el ends here