Tools for grepping in Emacs

Jonathan Lange jml at mumak.net
Tue Apr 28 09:59:17 BST 2009


On Mon, Apr 13, 2009 at 5:52 AM, Karl Fogel <karl.fogel at canonical.com> wrote:
> Jonathan Lange <jml at mumak.net> writes:
>> My elisp-fu is very weak, and I needed a lot of help even to get this
>> much done. I'd love some feedback on how the code could be nicer, or
>> suggestions on how this should best be maintained / shared with the
>> rest of the Bazaar / Emacs community.
>
> Your elisp is excellent, for someone who says his elisp-fu is weak!

Thanks, although I can't take much credit.

> A few comments below.
>
>> (provide 'bzr-tools)
>>
>> (defun search-upwards (filename starting-path)
>>   "Search for `filename' in every directory from `starting-path' up."
>>   (defun parent-dir (path)
>>     (file-name-directory (directory-file-name path)))
>>
>>   (let ((path (file-name-as-directory starting-path)))
>>     (if (file-exists-p (concat path filename))
>>         path
>>       (let ((parent (parent-dir path)))
>>         (if (string= parent path)
>>             nil
>>           (search-upwards filename parent))))))
>
> It's unusual to have a defun within a defun;

Not only that, it defines a global anyway, so I'm not even masking the
inner defun!

> more common, I think, is to
> use 'lambda' and 'funcall', e.g.:
>
>   (let ((parent-dir (lambda (path)
>                       (file-name-directory (directory-file-name path)))))
>     (funcall parent-dir "/foo/bar/"))
>
> However, I'm not sure the extra readability of a named function is even
> worth it.  You could just do
>
>  (let ((parent (file-name-directory (directory-file-name path))))
>    ...)
>
> and the meaning would be obvious anyway.
>

Obvious to you maybe. To a casual, Python-influenced reader,
(file-name-directory (directory-file-name path)) doesn't scream out
"get the parent directory of path".

It turns out, however, that Emacs 23 has `locate-dominating-file',
which does exactly what I want. Since I use Emacs 23 personally, I've
just deleted my snippets of code.

>> ;; Run 'code' in directory 'dirname'.
>> ;; Copied from twisted-dev.el
>> (defmacro with-cd (dirname &rest code)
>>   `(let ((old-dirname default-directory)
>>        (start-buffer (current-buffer)))
>>      (cd ,dirname)
>>      (unwind-protect (progn , at code)
>>        (let ((end-buffer (current-buffer)))
>>          (set-buffer start-buffer)
>>          (cd old-dirname)
>>          (set-buffer end-buffer)))))
>>

Offlist, someone pointed out that I could just set default-directory
instead of this convoluted macro.

>>
>> ;; Run 'code' at the root of the branch which dirname is in.
>> (defmacro at-branch-root (dirname &rest code)
>>   `(with-cd (search-upwards ".bzr" (expand-file-name ,dirname)) , at code))
>>
>>
...
> (Why not using 'at-branch-root' in 'bzr-grep', by the way?)
>

Simple mistake.

>> (defun grep-branch (expression)
>>   (interactive "MSearch for: ")
>>   (at-branch-root "."
>>    (grep-find (format "bzr ls -V --null | xargs -0 grep -In %s" expression))))
>>
>> (defun branch-todo ()
>>   (interactive)
>>   (with-cd (search-upwards ".bzr" (expand-file-name "."))
>>    (compile "bzr todo" t)))
>
> The only other thing I'd suggest is giving a common prefix to all the
> symbols, something beginning with 'bzr-...', if you want to distribute
> the code.  Prefixes are Emacs's module system, believe it or not :-).
>

And PHP's too!

"Those who cannot remember the past are condemned to repeat it"

I've attached the updated version. The other significant change is
that I use `shell-quote-argument' to wrap up the regular expression
that the user provides.

Thanks heaps to everyone who sent me feedback. It was very helpful and
very encouraging.

Happy chording!
jml
-------------- next part --------------
A non-text attachment was scrubbed...
Name: bzr-tools.el
Type: text/x-emacs-lisp
Size: 2153 bytes
Desc: not available
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20090428/52884e25/attachment.bin 


More information about the bazaar mailing list