Sublime Text 2's "Goto Anything" (or instant search) for Emacs?

Vicky Chijwani picture Vicky Chijwani · Feb 6, 2013 · Viewed 8.3k times · Source

I tried out Sublime Text 2 recently, and I found Goto Anything superbly useful for navigating source code (Ctrl-P file@symbol seems to work really well). Is there something similar for Emacs? Preferably something that just works, without a ton of custom elisp.

What I've tried so far:

  1. I've seen Helm and Anything, but as far as I understand neither of them is capable of actual "instant" search (see edit below).

  2. I've used multi-occur-in-matching-buffers, but it too seems unable to satisfy the "instant" criterion.

  3. imenu / idomenu works well for single files, but doesn't work across files.

I currently use #2 and #3 together, as a poor substitute for Goto Anything.

If not an exact clone of Goto Anything, then I could make do with a naive instant search solution (one that searches for a given string across all open buffers and displays results dynamically). So that's acceptable too.

I use Emacs 24.2, so any v24-only elisp is also fine.

EDIT: I gave Helm another shot, at event_jr's suggestion, and I found that it does support instant searching across all open buffers. helm-multi-occur + helm-follow-mode comes surprisingly close to meeting my needs, the only minor issues being (at the risk of sounding nit-picky):

  • I haven't found a way to turn on helm-follow-mode automatically when I run helm-multi-occur. I have to invoke it manually with C-c C-f. Anyone care to take a shot at this with a snippet of elisp? (see edit #2 below)

  • it isn't "intelligent" like ST2's Goto Anything (i.e., it doesn't understand "symbols" in source code, like Goto Anything does).

EDIT #2: Now I've got most of Goto Anything, thanks to event_jr's answer below (and of course, thanks to Helm's creator, Thierry Volpiatto). I recommend it heartily to anyone looking for a similar feature. Below is the elisp I'm currently using:

;; instant recursive grep on a directory with helm
(defun instant-rgrep-using-helm ()
  "Recursive grep in a directory."
  (interactive)
  (let ((helm-after-initialize-hook #'helm-follow-mode))
    (helm-do-grep)))


;; instant search across all buffers with helm
(defun instant-search-using-helm ()
  "Multi-occur in all buffers backed by files."
  (interactive)
  (let ((helm-after-initialize-hook #'helm-follow-mode))
    (helm-multi-occur
     (delq nil
           (mapcar (lambda (b)
                     (when (buffer-file-name b) (buffer-name b)))
                   (buffer-list))))))

;; set keybindings
(global-set-key (kbd "C-M-s") 'instant-search-using-helm)
(global-set-key (kbd "C-M-S-s") 'helm-resume)
(global-set-key (kbd "C-M-g") 'instant-rgrep-using-helm)

Answer

event_jr picture event_jr · Feb 6, 2013

Just use helm.

It is perhaps more configuration than you asked for, but once you get it configured how you like, it should be quite comfortable. Very much like Emacs ;).

And you should file a bug with Thierry for getting some more newbie friendly defaults. He is quite responsive with issues.

helm-multi-occur

Primarily multi-buffer interactive "occur" is provided through helm-multi-occur. If you execute the command, you'll notice that you have to pick some buffers first (use C-SPC to select from the list, M-SPC to select all). Then you can enter your query at the next prompt. It's easy to make your own version that skips the buffer selection like so:

(eval-after-load "helm-regexp"
    '(setq helm-source-moccur
           (helm-make-source "Moccur"
               'helm-source-multi-occur :follow 1)))

(defun my-helm-multi-all ()
  "multi-occur in all buffers backed by files."
  (interactive)
  (helm-multi-occur
   (delq nil
         (mapcar (lambda (b)
                   (when (buffer-file-name b) (buffer-name b)))
                 (buffer-list)))))

helm-buffers-list

Often you don't care about the exact occurrences of the query string, but want a list of all buffers that contain it.

helm-buffers-list has some tricks up its sleeve. The first symbol you specify is filtering by major-mode, and you can use the "@" prefix to narrow the list to buffers that contain a string.

To wit, "ruby @prompt" will show you a list of buffers whose major-mode contains "ruby" and whose contents contains "prompt". Or you can just use "@prompt" to show all buffers that contain "prompt".


Powerful and comfortable once you get used to it.


EDIT modified my-helm-multi-all to enable helm-follow-mode.

EDIT 2 update helm-follow-mode code to reflect helm changes.

EDIT 3 updated again to reflect helm changes