25

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)
Community
  • 1
  • 1
Vicky Chijwani
  • 10,191
  • 6
  • 56
  • 79
  • 1
    This would probably be a better question for superuser.com – Tim Radcliffe Feb 06 '13 at 10:26
  • 10
    No, questions about Emacs in the context of programming are 100% in scope of SO – Mirzhan Irkegulov Feb 06 '13 at 10:59
  • Why not just stick with sublime? – event_jr Feb 06 '13 at 12:16
  • 6
    @event_jr I'm not going to switch from Emacs to Sublime Text just because it doesn't have Goto Anything. I'll gain one thing and lose a million if I do that :) – Vicky Chijwani Feb 06 '13 at 12:38
  • @VickyChijwani What are some killer features of Emacs missing in ST2? – event_jr Feb 06 '13 at 13:01
  • 3
    @event_jr It's not about specific "killer features". In fact, Emacs doesn't even _have_ a fixed set of "features". It's about the editor in general. I love how easy it is to modify _every last shred of Emacs_. The infinite customizability constantly compounds and pays off. Every little customization I make brings it closer to my mental image of the "ideal" editor. In a sense, _that is Emacs' **real** killer feature_. If I were using ST2, on the other hand, my productivity would quickly saturate to the maximum level possible with it. – Vicky Chijwani Feb 06 '13 at 13:54
  • Helm and Anything together? Helm is basically a continuation of the now defunct Anything project. – Bozhidar Batsov Feb 06 '13 at 16:05
  • @BozhidarBatsov I meant I've tried out Anything and Helm independently, at different points of time. – Vicky Chijwani Feb 06 '13 at 17:13
  • Your comment about "symbols", did you mean method definitions a la imenu? – event_jr Feb 07 '13 at 00:48
  • Yes, those, and perhaps other definitions too if possible (constants, etc). – Vicky Chijwani Feb 07 '13 at 00:51
  • Emacs supports symbols directly through its regular expression engine. You use `\_<` to mark anchor to beginning to a symbol and `\_>` to anchor to the end. – event_jr Feb 07 '13 at 01:39

5 Answers5

19

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

event_jr
  • 17,467
  • 4
  • 47
  • 62
  • This is great. I tried Helm again, at your suggestion, and I think `my-helm-multi-all` + `helm-follow-mode` (accessed via `C-c C-f` in a Helm buffer) comes pretty close to what I want. So thank you for that, firstly. Now the only major issue is that I have to manually turn on `helm-follow-mode` when I run `my-helm-multi-all`; is there a way to have it turn on automatically? (note that Helm doesn't seem to have a `helm-mode-hook`, so that approach won't work). – Vicky Chijwani Feb 06 '13 at 17:07
  • Can this (i.e., turning on `helm-follow-mode` automatically in Helm buffers) be done using `defadvice` on `helm-mode` or `helm-multi-occur`? – Vicky Chijwani Feb 06 '13 at 18:59
  • I edited my answer to show how to do it with a provided hook. `defadvice` is akin to a chainsaw, it's rarely necessary to solve your problems. – event_jr Feb 07 '13 at 00:38
  • Nice, this gives me like 95% of what I wanted. Thank you so much. It'd be silly to pursue this any longer for that _one_ remaining annoyance. I'm accepting your answer. And I think I should learn some elisp myself :) – Vicky Chijwani Feb 07 '13 at 00:56
  • 2
    Hi. I tried this and i get `Wrong type argument: number-or-marker-p, nil`. I'm using Emacs 24. – Gonçalo Marrafa Apr 09 '13 at 11:16
  • thanks for great info. I'm big fan of source insight. helm + gtags help me with quick source code browsing. – sudurais Nov 22 '13 at 02:27
  • 1
    It seems as though a recent update to helm has broken this again. – nispio Sep 11 '14 at 22:39
  • 1
    Indeed. @nispio I've fixed up the code as per your rejected edit. – event_jr Oct 01 '14 at 01:51
  • It seems that `@foo` in `helm-buffers-list` searches the name of the buffer for that text, not the contents. Perhaps this has changed, or there's a setting I'm missing? – Wilfred Hughes Mar 15 '16 at 13:35
  • Are you using `helm-buffers-list`? – event_jr Mar 16 '16 at 13:14
7

Emacs has Projectile satisfy your need:

  • jump to a file in project
  • multi-occur in project buffers
L42y
  • 972
  • 1
  • 9
  • 18
  • It can also jump to tests and project buffers, grep in projects, replace in projects, regenerate the etags table and other cool stuff. Projectile also has a helm backend. – Bozhidar Batsov Feb 06 '13 at 16:02
  • As I said in my question, I've used `multi-occur` and it doesn't do *instant searching* ("instant" in the sense of "Google Instant"). With `multi-occur`, you have to type in your entire query, press `RET`, and only then you get to see the results. Also, if you need to change your search query you have to run `multi-occur` again with your new search term. That's not what I'm looking for. – Vicky Chijwani Feb 06 '13 at 19:03
  • Vicky: FWIW, I had no idea what "instant searching" was supposed to mean until you mentioned "Google Instant" -- and only then after I Googled the phrase "Google Instant" to see what *that* was. The expression "instant searching" is *extremely* vague unless you already know what Google means by it. I would suggest something like "dynamic search results" would be a much clearer description. – phils Feb 07 '13 at 03:47
  • @phils that is exactly why I linked to videos demonstrating the feature, right in my question: https://www.youtube.com/watch?v=36NIIgcrYzE and https://www.youtube.com/watch?v=Q0OtfjgnoLA#t=1m00s – Vicky Chijwani Feb 21 '13 at 16:46
1
  1. Heml is far from the fuzzy searching of ST3.

  2. Fiplr looks promising but doesn't work on my laptop (see first issue on the github)

  3. Simp.el looks like Fiplr but doesn't work either on my end.

  4. Projectile works for me! Here's your solution!

I used also ido-mode and flx-ido for the fuzzy searching,

and for the vertical way of displaying results I use this in my .emacs:

;; Display ido results vertically, rather than horizontally
(setq ido-decorations (quote ("\n-> " "" "\n   " "\n   ..." "[" "]" " [No match]" " [Matched]" " [Not readable]" " [Too big]" " [Confirm]")))
(defun ido-disable-line-truncation () (set (make-local-variable 'truncate-lines) nil))
(add-hook 'ido-minibuffer-setup-hook 'ido-disable-line-truncation)
(defun ido-define-keys () ;; C-n/p is more intuitive in vertical layout
  (define-key ido-completion-map (kbd "C-n") 'ido-next-match)
  (define-key ido-completion-map (kbd "C-p") 'ido-prev-match))
(add-hook 'ido-setup-hook 'ido-define-keys)
David 天宇 Wong
  • 3,724
  • 4
  • 35
  • 47
0

Icicles offers some features that are similar to what it seems you are looking for.

  1. C-x b and C-x C-f, to choose buffers or files, allow multi-completion: you can type a pattern to match the buffer/file name and/or a pattern to match content in the buffer/file. Candidates are filtered incrementally as you type (what you call "instant" is what Emacs calls "incremental"). You can refine either or both search patterns progressively, narrowing the choices in different ways. You can visit any number of buffers/files that match, at the same time. You can also use the same method to search the marked files in Dired: C-F.

  2. C-c `(icicle-search) incrementally searches across multiple buffers or files. Again, progressive refinement etc.

The main difference between #1 and #2 is this:

  • For #1, you just want to find matching buffers or files. You don't care immediately about finding particular occurrences --- any match suffices.

  • For #2, you provide the buffers or files to search, and you want to navigate among search hits.

You can also use #1 to locate the buffers and files you want, then search their contents: The content-matching pattern you last used is available as the search pattern for Isearch (C-s).

Drew
  • 29,895
  • 7
  • 74
  • 104
0

for emacs I customize and modify this solution (for use install helm):

(defun helm-occur-from-point (initial-value)
  "Invoke `helm-occur' from point."
  (interactive)
  (let ((input initial-value)
        (bufs (list (buffer-name (current-buffer)))))
    ;; (isearch-exit)
    (helm-occur-init-source)
    (helm-attrset 'moccur-buffers bufs helm-source-occur)
    (helm-set-local-variable 'helm-multi-occur-buffer-list bufs)
    (helm-set-local-variable
     'helm-multi-occur-buffer-tick
     (cl-loop for b in bufs
              collect (buffer-chars-modified-tick (get-buffer b))))
    (helm :sources 'helm-source-occur
          :buffer "*helm occur*"
          :history 'helm-grep-history
          :input input
          :truncate-lines t)))

(defun get-point-text ()
    "Get 'interesting' text at point; either word, or region"
    (if mark-active
        (buffer-substring (mark) (point))
      (thing-at-point 'symbol)))
  (defun helm-occur-1 (initial-value)
    "Preconfigured helm for Occur with initial input."
    (helm-occur-from-point initial-value))
  (defun bk-helm-occur ()
    "Invoke helm-occur with initial input configured from text at point"
    (interactive)
    (helm-occur-1 (get-point-text)))
  (global-set-key (kbd "M-s-o") 'bk-helm-occur)

primary it based on @see https://news.ycombinator.com/item?id=6872508 but on last helm versions not work but fixed with my changes (just copy/paste from some internal helm modules)

user1633548
  • 99
  • 1
  • 5