An Extensive, But Not Comprehensive, Look At Finding Files
The Problem
Finding files can be a tricky business. If you have a number of different directories where your files can be stored, how can you open a particular file as quickly as possible. The standard find-file interface is pretty good if you’re already in the correct directory as it will do tab completion on anything you have already typed in similar to how a decent shell will.
ido improves this interface. If you enter part of a filename that has already been seen, even if it is in a different directory it will be offered to you. I enable flex-matching so that ido will match files containing the characters which have been entered anywhere so long as they are in the correct sequence. e.g. eod will match hello-world.c.
(ido-mode t) (setq ido-enable-flex-matching t) (setq ido-create-new-buffer 'always)
I understand that icicles does even better here, enabling you to filter out matches but as I’m not (yet) using it myself, I can’t say much more about it.
If you need more help in finding your files, what other options are there?
Bookmarks
I hadn’t heard of Bookmarks before I saw a a post on them recently. I use them in conjunction with directory aliases1.
From the emacs manual:
Bookmarks are somewhat like registers in that they record positions you can jump to. Unlike registers, they have long names, and they persist automatically from one Emacs session to the next. The prototypical use of bookmarks is to record โwhere you were readingโ in various files.
Filecache
Sacha Chua wrote a post about navigating your source tree using ido and filecache. I’m not sure what additional benefits
filecache offers over vanilla ido as ido finds files in other directories too.
Tags
The traditional way for finding function definitions is with Emacs Tags. Once you have generated the tags file you can go from call site to function definition with M-. (find-tag)
.
When I go on holiday I leave a link to the current method I am working on in an orgfile. Then when I come back, I can click on the current work link and press M-.
and I’ll be right back where I left off.
[[SomeClass::SomeMethod][Current Work]] * Work Projects *** A Specific Project SomeClass::SomeMethod Some notes about what I was doing...
I set the tags file so I don’t need to find that file.
(setq tags-file-name "/path/to/tags/file")
1. Directory Aliases
(require 'ido) (require 'dired) (defconst *web-top* "~/websites") (defun web (path) (concat *web-top* path))
dired-dirs
is an associating list mapping aliases to directories. I need to use (list ...)
to construct it so that (web ...)
expands correctly.
(defconst *dired-dirs* (list (cons "mrc-theme" (web "/blog/mrc/wp-content/themes/mrc")) (cons "mrc-pages" (web "/pages/mrc")) (cons "webtest" (web "/test")))) (defconst *dired-aliases* (mapcar (lambda (e) (car e)) *dired-dirs*))
dired-open-alias
uses ido-completing-read
to choose between the aliases I defined earlier.
(defun dired-open-alias (&optional alias) (interactive) (unless alias (setq alias (ido-completing-read "Alias: " *dired-aliases* nil t))) (if (and (stringp alias) (> (length alias) 0 )) (let ((pair (assoc alias *dired-dirs*))) (if pair (dired (cdr pair)) (error "Invalid alias %s" alias))) (error "Invalid alias %s" alias)))
I really like f2 as a prefix key. The default binding is related to two-column mode, which I never use, and you can use it without pressing shift, ctrl or alt. I have this defined in my-defaults.el (which probably isn’t strictly necessary but at least it names the prefix map).
(defvar f2-prefix-map nil) (setq f2-prefix-map (make-sparse-keymap)) (global-set-key [f2] f2-prefix-map)
<f2> d
calls up the list of directory aliases.
(global-set-key (kbd "<f2> d") 'dired-open-alias)
Thanks for linking! I’ll check out the ido features to see if I can control which directories are searched.
I find filecache to be incredibly useful for managing multiple source trees, because I can open files under a certain directory using one keyboard shortcut and files under a different directory with another. =)
How about anything mode?
I don’t know If you meant this, but you can filter matches with ido mode.
Say you are looking for .c files, you can C-x C-f .c C-SPC
If it’s not what you meant, just ignore me
Hi folks,
@Sacha – you’re welcome! (it was a good post). The autofinding in ido seems to be a bit hit and miss to me. After waiting for half a second for the search in any directory it mostly finds what I wanted, but occasionally it doesn’t.
@mazirian – I’ve heard very good things about anything mode and it is definitely on my list of things to check out.
@Reynaldo – fantastic, I didn’t know about that and it is exactly what I was talking about. Thanks very much for your input.
@Reynaldo As you said, I try it : C-x C-f .c C-SPC , but nothing happens. I think this is because I bound C-SPC to set-mark. Can you tell me C-SPC means what function? Thanks.
Hi Kevin,
I also have C-SPC bound to set-mark. Have you enabled ido mode with (ido-mode t) or something similar?
When following Reynaldo’s tip, the list has already been narrowed after typing .c. Pressing C-SPC after this re-enters ido mode with the new filtered list that you can now apply other filters to or select between elements using the arrow keys.
Hi Jared,
I misunderstood what Reynaldo said. With your explain, I know it. Now it works well.^_^
You’re welcome Kevin ๐
Wrapping a simple function in another simple function?
(mapcar (lambda (e) (car e)) *dired-dirs*)
No need for that. ๐
(mapcar ‘car *dired-dirs*)
Hi Chris,
That’s another thing I noted from the perlsofwisdom blog I linked to in my clever emacs lisp post (although isn’t it more usual to use #’ in this instance rather than just ‘ ?). I really need to tidy some of my code up at some stage.
Cheers
#’ is mostly a carryover from Common Lisp that doesn’t really do anything in Elisp. It’s the same as using (function), which, in Elisp, is only used to tell the compiler to follow through to that symbol and compile it too. In practice, not only is this almost always unnecessary because the symbol would have already been compiled anyway (defined in a file that was already being compiled), but I’ve found that the compiler doesn’t *actually* quite behave this way.
And also since it’s uglier and requires an extra character, I never bother using it in Elisp at all. ๐
In this specific case, car is a built-in, so using #’ couldn’t do anything unless you’re a crazy person who has somehow redefined car in lisp.
Good to know! And although I might be crazy enough to redifine identity, I’m not crazy enough to redefine car ๐
Cheers