When using Emacs I’m often switching buffers or opening files, so I’m always on the lookout for ways to make those operations more pleasant.
A while ago I tried Anything.el. Anything is like Quicksilver for Emacs. It’s powerful but I found it confusing to set up. Emacs-Fu had a blog post about configuring anything.el, and I used that for my setup.
For the most part, I use it instead of find-file
:
When I saw that Helm was the successor to Anything, I decided to switch to Helm and simplify my setup. I was pleasantly surprised by how easy Helm was to set up compared to Anything. Here’s my setup, binding Alt+T (inspired by Textmate) to invoke helm-for-files
:
(require 'helm-files) (setq helm-idle-delay 0.1) (setq helm-input-idle-delay 0.1) (setq helm-locate-command "locate-with-mdfind %.0s %s") (loop for ext in '("\\.swf$" "\\.elc$" "\\.pyc$") do (add-to-list 'helm-boring-file-regexp-list ext)) (define-key global-map [(alt t)] 'helm-for-files))
This uses the default helm-for-files
setup, which lists, in order:
- ffap (find file at point)
- buffers (names of all open buffers)
- recentf (recently opened files)
- bookmarks (which I don’t use)
- files-in-current-dir (like find-file)
- locate (files in other directories)
However, I don’t want locate
for all files on my disk; I want my home directory, minus a few subdirectories that contain files I never open. For this, I use a shell script to filter the results. On a Mac, use mdfind -name
instead of locate
; it’s updated in real time instead of nightly. I use a more complicated script that also handles patterns like foo/bar.txt
:
#!/bin/bash # Note: updated 2014-03-28 to handle patterns like foo/bar.txt suffix=$(basename "$@") pattern="${suffix}*" if [ "$@" = "$suffix" ]; then # If there's no folder then it should be a substring pattern="*${suffix}*" fi mdfind -onlyin $HOME "kMDItemFSName = '$pattern'cd" \ | fgrep -i "$@" \ | grep -v "$HOME/Library/" \ | grep -v "$HOME/Pictures/" \ | grep -v "$HOME/Music/" \ | sed -e "s|^$HOME|~|"
(Note: if you want to use mdfind to search contents and not only filenames, take a look at helm-source-mac-spotlight
.)
More thoughts:
- Ideally, I don’t want to have to think about whether I’ve already opened a file or not. In practice I do treat switching buffers and opening files differently. I use Alt+J and Alt+K for switching buffers in the same directory (see this blog post). I sometimes use
ido-find-file
(bound to Alt+O) for opening a file in the same directory, but I’m increasingly usinghelm-for-files
even in the same directory. - Since
mdfind
is so fast with an SSD, I reduced the initial delay from 300ms to 100ms. That made it feel much better. You may need to experiment with the delay setting. - The same file can be in the buffers list, the recent files list, the current directory list, and the locate list. I’d like to see each file only once. I’m sure helm can filter these out but I don’t know how.
- It'd be nice to not have it print out the full path every time, but I haven't figured out how to do that.
There are so many amazing things Helm can do; I’m only using it for this one feature, and it’s been great.
Update: [2014-03-28] I updated the "locate" script I use to allow finding patterns like foo/bar.txt
. Normally, mdfind
won't find these because it only sees bar.txt
.
Update: [2014-05-27] Also see LocateFilesAnywhere on EmacsWiki if you're looking for Emacs packages that let you find files from anywhere, not only the current directory. I'm no longer using the above setup.
Labels: emacs
thanks for the recommendation! helm definitely looks like a worthy successor to ido, anything, and others. i'm now using it for finding files/switching buffers, like you, and for minibuffer history isearch. my only tweak beyond your settings was to add helm-c-source-find-files to helm-for-files-preferred-list so i could type (partial) paths occasionally, since the locate source doesn't seem to handle those well.
looking forward to diving in more!
I've been using ido for a long time but I'll give this a shot
Dont understand how you integrate your bash script with helm - where doyou put the bash script and how do you refer to it ?
Hi gogleyedboggle,
You can put the bash script anywhere but you need to set helm-c-locate-command to point to it.
How did you know about mdfind? Is there anywhere I can learn about these mac specific unix commands?
n: good question; I don't remember! I was probably browsing some Mac helpful hints site when I found it. I also occasionally look in /usr/bin to see what's there that I'm not familiar with.
You don't need the bash script at all you can configure Emacs to use mfind (spotlight) as its locate command:
(setq locate-command "mdfind")
You can read more at http://emacsredux.com/blog/2013/07/05/locate/
Hi PuercoPop, I use the bash script because I want to modify the results of mdfind. You can see in my script, I filter out certain folders.
Thanks for the posting.
Here's a varation on your script that let's one search for N terms in the complete path. It still requires that the first term appear in the basename of the file.
mdfind_query="kMDItemFSName==\"*$1*\"cd"
shift
grep_query='cat'
for bit in "$@" ; do
grep_query="$grep_query | grep '$bit'"
done
echo $grep_query >> /tmp/foo1
mdfind -onlyin $HOME "$mdfind_query" \
| eval "$grep_query" \
| grep -v "$HOME/Library/" \
| grep -v "$HOME/Pictures/" \
| grep -v "$HOME/Music/" \
| sed -e "s|^$HOME|~|"
Thanks exuberance! That looks useful.
Thanks. I do also this
grep_query="$grep_query | grep -i '$bit'"
It will be faster to type when theres no case sensitive grep
Post a Comment