Search code examples
emacssublimetextemacs24dot-emacsemacs-helm

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


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)

Solution

  • 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