HippieExpand

HippieExpand looks at the word before point and tries to expand it in various ways including expanding from a fixed list (like `‘expand-abbrev’’), expanding from matching text found in a buffer (like `‘dabbrev-expand’’) or expanding in ways defined by your own functions. Which of these it tries and in what order is controlled by a configurable list of functions.

But what does it have to do with hippies?

I do not know what it has to do with hippies, maybe it just has to do with the fact that it might seem crazy and try different ways of expanding? Anyway, read the source code or visit the following page to get some background: http://www.xemacs.org/Documentation/packages/html/edit-utils_23.html. Maybe you should ask the author, Anders Holst: http://www.sics.se/~aho/index.shtmlMathiasDahl 2004-08-12 20:49 UTC

bind hippie expand to a useful key

To bind hippie-expand to M-<spc> put the following in your .emacs

    (global-set-key "\M- " 'hippie-expand)

Create a hippie-expand function with a given list of strings

    (setq dcsh-command-list '("all_registers"
                              "check_design" "check_test" "compile" "current_design"
                              "link" "uniquify"
                              "report_timing" "report_clocks" "report_constraint"
                              "get_unix_variable" "set_unix_variable"
                              "set_max_fanout"
                              "report_area" "all_clocks" "all_inputs" "all_outputs"))
    
    (defun he-dcsh-command-beg ()
      (let ((p))
        (save-excursion
          (backward-word 1)
          (setq p (point)))
        p))
    
    (defun try-expand-dcsh-command (old)
      (unless old
        (he-init-string (he-dcsh-command-beg) (point))
        (setq he-expand-list (sort
                              (all-completions he-search-string (mapcar 'list dcsh-command-list))
                              'string-lessp)))
      (while (and he-expand-list
              (he-string-member (car he-expand-list) he-tried-table))
        (setq he-expand-list (cdr he-expand-list)))
      (if (null he-expand-list)
          (progn
            (when old (he-reset-string))
            ())
        (he-substitute-string (car he-expand-list))
        (setq he-tried-table (cons (car he-expand-list) (cdr he-tried-table)))
        (setq he-expand-list (cdr he-expand-list))
        t))

Create a keybinding with a list of hippie-expand functions

    (global-set-key [(meta f5)] (make-hippie-expand-function
                               '(try-expand-dcsh-command
                                 try-expand-dabbrev-visible
                                 try-expand-dabbrev
                                 try-expand-dabbrev-all-buffers) t))

MicheleBini has also a nice recipe for a hippie-expand function

Add a new expansion

If hippie-expand fails to find any satisfactory expansion, you may want to add a new one by using function `‘inverse-add-global-abbrev’’. The following should probably be the last function in `‘hippie-expand-try-functions-list’’:

    (defun my-try-add-expansion (old)
      "Add a new global abbrev"
      (he-init-string (he-dabbrev-beg) (point))
      (or (inverse-add-global-abbrev 1)
	  (he-reset-string)))

DenisHowe 2007-09-18

Expanding Tags

Another useful expansion is to expand tags. I use it as a fallback after the dabbrev expansions fail.

(require 'cc-mode)

(defun he-tag-beg ()
  (let ((p
         (save-excursion 
           (backward-word 1)
           (point))))
    p))

(defun try-expand-tag (old)
  (unless  old
    (he-init-string (he-tag-beg) (point))
    (setq he-expand-list (sort
                          (all-completions he-search-string 'tags-complete-tag) 'string-lessp)))
  (while (and he-expand-list
              (he-string-member (car he-expand-list) he-tried-table))
              (setq he-expand-list (cdr he-expand-list)))
  (if (null he-expand-list)
      (progn
        (when old (he-reset-string))
        ())
    (he-substitute-string (car he-expand-list))
    (setq he-expand-list (cdr he-expand-list))
    t))

tags-complete-tag

I found that this function is not available in the latest etags.el. Here is what I put in my .emacs

   (defun tags-complete-tag (string predicate what)
     (save-excursion
     ;; If we need to ask for the tag table, allow that.
     (if (eq what t)
	(all-completions string (tags-completion-table) predicate)
      (try-completion string (tags-completion-table) predicate))))

– Sudheer Koganti

Substring Expansion

When doing lisp programming standard dabbrev is less useful, because Emacs has no namespaces, so in a package symbols begin with the same prefix, therefore the same prefix has to be typed again and again if one wants to complete symbols from the package. As with IswitchBuffers it is much more effective if a unique substring can be typed to get to the desired symbol.

Dabbrev offers nothing in this regard, so I turned to Hippie Expand which I had never used before.

Here’s a function based on hippie expand dabbrev expansion which performs substring expansion:

(defun try-my-dabbrev-substring (old)
  (let ((old-fun (symbol-function 'he-dabbrev-search)))
    (fset 'he-dabbrev-search (symbol-function 'my-dabbrev-substring-search))
    (unwind-protect
        (try-expand-dabbrev old)
      (fset 'he-dabbrev-search old-fun))))


(defun my-dabbrev-substring-search (pattern &optional reverse limit)
  (let ((result ())
	(regpat (cond ((not hippie-expand-dabbrev-as-symbol)
		       (concat (regexp-quote pattern) "\\sw+"))
		      ((eq (char-syntax (aref pattern 0)) ?_)
		       (concat (regexp-quote pattern) "\\(\\sw\\|\\s_\\)+"))
		      (t
		       (concat (regexp-quote pattern)
			       "\\(\\sw\\|\\s_\\)+")))))
    (while (and (not result)
		(if reverse
		     (re-search-backward regpat limit t)
		     (re-search-forward regpat limit t)))
      (setq result (buffer-substring-no-properties (save-excursion
                                                     (goto-char (match-beginning 0))
                                                     (skip-syntax-backward "w_")
                                                     (point))
						   (match-end 0)))
      (if (he-string-member result he-tried-table t)
	  (setq result nil)))     ; ignore if bad prefix or already in table
    result))

I did substring expansion only when input begins from delimited chars. Try M-x hippie-expand-dabbrev-limited-chars in Lisp:hippie-exp-ext.elrubikitch

Flexible match expansion

I find it hard to give this hack a name but I know a similar feature in ido is called “flexible” matching, or similar, so I will use that in lack of a better term.

The idea is to let the user type any characters in the word he wants to complete/expand. So, for example, to match “thisLongWord” the user can type “this” (which normal prefix matching would also match) or “long” or “word” (like substring matching) but also “tld” or “lwd”. That way it can be easier to match long words which have the same prefix. In a way, the user can create special abbreviations on the fly. Or something like that… 😊

To try it out, evaluate the following expressions:

(defun try-expand-flexible-abbrev (old)
  "Try to complete word using flexible matching.

Flexible matching works by taking the search string and then
interspersing it with a regexp for any character. So, if you try
to do a flexible match for `foo' it will match the word
`findOtherOtter' but also `fixTheBoringOrange' and
`ifthisisboringstopreadingnow'.

The argument OLD has to be nil the first call of this function, and t
for subsequent calls (for further possible completions of the same
string).  It returns t if a new completion is found, nil otherwise."
  (if (not old)
      (progn
	(he-init-string (he-lisp-symbol-beg) (point))
	(if (not (he-string-member he-search-string he-tried-table))
	    (setq he-tried-table (cons he-search-string he-tried-table)))
	(setq he-expand-list
	      (and (not (equal he-search-string ""))
		   (he-flexible-abbrev-collect he-search-string)))))
  (while (and he-expand-list
	      (he-string-member (car he-expand-list) he-tried-table))
    (setq he-expand-list (cdr he-expand-list)))
  (if (null he-expand-list)
      (progn
	(if old (he-reset-string))
	())
      (progn
	(he-substitute-string (car he-expand-list))
	(setq he-expand-list (cdr he-expand-list))
	t)))

(defun he-flexible-abbrev-collect (str)
  "Find and collect all words that flex-matches STR.
See docstring for `try-expand-flexible-abbrev' for information
about what flexible matching means in this context."
  (let ((collection nil)
        (regexp (he-flexible-abbrev-create-regexp str)))
    (save-excursion
      (goto-char (point-min))
      (while (search-forward-regexp regexp nil t)
        ;; Is there a better or quicker way than using
        ;; `thing-at-point' here?
        (setq collection (cons (thing-at-point 'word) collection))))
    collection))

(defun he-flexible-abbrev-create-regexp (str)
  "Generate regexp for flexible matching of STR.
See docstring for `try-expand-flexible-abbrev' for information
about what flexible matching means in this context."
  (concat "\\b" (mapconcat (lambda (x) (concat "\\w*" (list x))) str "")
          "\\w*" "\\b"))

(setq hippie-expand-try-functions-list
      (cons 'try-expand-flexible-abbrev hippie-expand-try-functions-list))

If you have any comments or suggestions for improvements, don’t hesitate to contact me.

Happy hacking!

MaDa

In order for the above to work with lisp code (which include hyphens) change string-at-point ‘word to ‘symbol and change “
w*” to “
w*-*”

ScottJaderholm

You mean ‘thing-at-point’? 😊

Also the code might be refactored a bit:

  • The (if (not old) ...) expression:
  (unless old
    (he-init-string (he-lisp-symbol-beg) (point))
    (if (not (he-string-member he-search-string he-tried-table))
        (setq he-tried-table (cons he-search-string he-tried-table)))
    (setq he-expand-list
          (and (not (equal he-search-string ""))
               (he-flexible-abbrev-collect he-search-string))))
  • And (if (null he-expand-list) ...) should be replace by ‘cond’ since both clauses have ‘progn’:
  (cond ((null he-expand-list)
         (if old (he-reset-string))
         nil)
        (t
         (he-substitute-string (car he-expand-list))
         (setq he-expand-list (cdr he-expand-list))
         t))
  • And last, ‘add-to-list’ is better than ‘setq’ (last line):
(add-to-list 'hippie-expand-try-functions-list 'try-expand-flexible-abbrev)

DuongNguyen

Don't add extra paren when expanding line with paredit

If you use paredit or something to add closing parens then hippie expand line can be problematic because you will end up with an extra closing paren. This could should fix that

(defadvice he-substitute-string (after he-paredit-fix)
  "remove extra paren when expanding line in paredit"
  (if (and paredit-mode (equal (substring str -1) ")"))
      (progn (backward-delete-char 1) (forward-char))))

ScottJaderholm


Expansion menu

The following is an approach for obtaining the complete list of possible expansions from hippie-expand, and letting the user select the one they want via the ido interface.

    (defun my-hippie-expand-completions (&optional hippie-expand-function)
      "Return the full list of possible completions generated by `hippie-expand'.
    The optional argument can be generated with `make-hippie-expand-function'."
      (let ((this-command 'my-hippie-expand-completions)
            (last-command last-command)
            (buffer-modified (buffer-modified-p))
            (hippie-expand-function (or hippie-expand-function 'hippie-expand)))
        (flet ((ding)) ; avoid the (ding) when hippie-expand exhausts its options.
          (while (progn
                   (funcall hippie-expand-function nil)
                   (setq last-command 'my-hippie-expand-completions)
                   (not (equal he-num -1)))))
        ;; Evaluating the completions modifies the buffer, however we will finish
        ;; up in the same state that we began.
        (set-buffer-modified-p buffer-modified)
        ;; Provide the options in the order in which they are normally generated.
        (delete he-search-string (reverse he-tried-table))))
    
    (defun my-ido-hippie-expand-with (hippie-expand-function)
      "Offer ido-based completion using the specified hippie-expand function."
      (let* ((options (my-hippie-expand-completions hippie-expand-function))
             (selection (and options
                             (ido-completing-read "Completions: " options))))
        (if selection
            (he-substitute-string selection t)
          (message "No expansion found"))))
    
    (defun my-ido-hippie-expand ()
      "Offer ido-based completion for the word at point."
      (interactive)
      (my-ido-hippie-expand-with 'hippie-expand))
    
    (global-set-key (kbd "C-c /") 'my-ido-hippie-expand)

This can easily be adapted to custom selections of hippie-expand compatible expansion functions:

    (defun my-ido-hippie-expand-filename ()
      "Offer ido-based completion for the filename at point."
      (interactive)
      (my-ido-hippie-expand-with
       (make-hippie-expand-function '(try-complete-file-name))))
    
    (global-set-key (kbd "C-c C-f") 'my-ido-hippie-expand-filename)

BUGS

I find a bug in try-expand-dabbrev-visible. When there is no further expansions, the cursor moves, which is very annoying. My simple fix:

(defun try-zwz-expand-dabbrev-visible (old)
  (save-excursion (try-expand-dabbrev-visible old)))

zwz

Report Emacs bugs using ‘M-x report-emacs-bug’. – DrewAdams