Saturday, October 23, 2010

Emacs Tip #38: Automatically diff binary files using hexl-mode

I saw a question on Stack Overflow which referred to a non-free (as in beer) file comparison application. My first thought was, "people pay for this?" Sure enough, there are quite a few non-free file comparison tools. Which leads to the question, what do they offer that I'm not getting in Emacs (using ediff). From the table on Wikipedia, it appears not too much. I updated a couple of fields which weren't specified for Ediff, and noticed there was a column for doing a binary diff.

I rarely have the need to do a binary diff, but I was curious what tools did to support it. The two I looked at just showed a standard diff of files using a display somewhat like hexl-mode.

I figured adding support for Ediff to automatically switch to a diff with hexl-mode would be easy enough, and it was. Here's the code to use. Note: it currently prompts to see if you want to do the diff in hexl-mode, remove the call to y-or-n-p to have it done automatically.

Edited Oct 24 to split the code into two pieces of advice to avoid an error.

(defvar ediff-do-hexl-diff nil
"variable used to store trigger for doing diff in hexl-mode")
(defadvice ediff-files-internal (around ediff-files-internal-for-binary-files activate)
"catch the condition when the binary files differ

the reason for catching the error out here (when re-thrown from the inner advice)
is to let the stack continue to unwind before we start the new diff
otherwise some code in the middle of the stack expects some output that
isn't there and triggers an error"
(let ((file-A (ad-get-arg 0))
(file-B (ad-get-arg 1))
(condition-case err
(if ediff-do-hexl-diff
(let ((buf-A (find-file-noselect file-A))
(buf-B (find-file-noselect file-B)))
(with-current-buffer buf-A
(hexl-mode 1))
(with-current-buffer buf-B
(hexl-mode 1))
(ediff-buffers buf-A buf-B))
(error (error-message-string err)))))))

(defadvice ediff-setup-diff-regions (around ediff-setup-diff-regions-for-binary-files activate)
"when binary files differ, set the variable "
(condition-case err
(setq ediff-do-hexl-diff
(and (string-match-p "^Errors in diff output. Diff output is in.*"
(error-message-string err))
(string-match-p "^\\(Binary \\)?[fF]iles .* and .* differ"
(y-or-n-p "The binary files differ, look at the differences in hexl-mode? ")))
(error (error-message-string err)))))

Sunday, October 10, 2010

Emacs Tip #37: fic-mode.el

I saw this question on Stack Overflow asking to highlight FIXME (and similar strings) in your code, but only in comments and strings.

The current fixme-mode.el found on the Emacs Wiki is kind of clunky (it's really a major mode) and awkward to read. So I took the challenge to write a new minor-mode which answers the question.

I give you fic-mode.el. It's named fic as an acronym for Fixme In Comments.

To use it, add something like the following to your .emacs:

(require 'fic-mode)
(add-hook 'c++-mode-hook 'turn-on-fic-mode)
(add-hook 'emacs-lisp-mode-hook 'turn-on-fic-mode)

Or, you can manually start it with M-x fic-mode.

Tuesday, June 22, 2010

vc-diff tweak

For a while I've noticed that 'vc-diff (C-x v =) would pop up a buffer when there were no differences, and that buffer would just say there weren't any differences. It finally got on my nerves, so I wrote this code which works around that issue.

I don't totally understand the vc code, but parts are set up to run asynchronously, and that is what was tripping up 'vc-diff in my case. The diff would come back as identical, but at the time of the callback, Emacs had already lost the window configuration - so it was forced to pop up a buffer.

(defvar my-vc-diff-pre-diff-window-configuration nil
"place to store window configuration,
because sometimes (usually) the vc-diff does things asynchronously")

(defadvice vc-diff-internal (around
"no changes still results in *vc-diff* buffer being shown, try to avoid that"
(setq my-vc-diff-pre-diff-window-configuration (current-window-configuration))
(let* ((res ad-do-it))
(when (or (null res)
(string-match-p "^No changes between"
(with-current-buffer "*vc-diff*"
(point-min) (point-max)))))
(set-window-configuration my-vc-diff-pre-diff-window-configuration))))

(defadvice vc-diff-finish (after
"when buffer says no differences, revert window configuration"
(when (and my-vc-diff-pre-diff-window-configuration
(string-match-p "^No changes between"
(with-current-buffer "*vc-diff*"
(point-min) (point-max)))))
(set-window-configuration my-vc-diff-pre-diff-window-configuration)))

Wednesday, April 28, 2010

Emacs Tip #36: Abort the minibuffer when using the mouse

A friend of mine loves using Emacs, but is always complaining about something. This time it's Emacs' behavior to keep the minibuffer active when you use the mouse to select another window. For example, you start doing a C-x C-f, click elsewhere, and do the C-x C-f again. Emacs will beep and tell you Command attempted to use minibuffer while in minibuffer.

After swallowing my, "well don't use the mouse" response I set about trying to fix it. I'm sure this has been solved before, I just didn't have enough google-fu to find the solution.

(defun stop-using-minibuffer ()
"kill the minibuffer"
(when (and (>= (recursion-depth) 1) (active-minibuffer-window))

(add-hook 'mouse-leave-buffer-hook 'stop-using-minibuffer)

Edited to add:
A commenter suggested using recursive minibuffers. I think that the resulting behavior is more confusing than helpful.

Yes, it avoids the error, but then you get this ever-increasing stack of minibuffers building up. If/when the user notices the minibuffer hanging around it'll be annoying (I've had people ask, "Why is it trying to find a file?" (because the minibuffer still shows Find file: /path/to/somewhere)). And heaven forbid they click in the minibuffer, type C-g and get back to the window configuration they were looking at when they started that command, which was ... 15 minutes ago.

Saturday, February 20, 2010

Emacs Tip #35: framemove

Ever wanted to easily navigate between frames? Perhaps using arrow keys? Surprisingly, this didn't exist before (AFAIK).

I wrote the package framemove to have the same usage as Emacs' built in windmove package. And, even better, it can integrate with windmove so that when you run out of windows to move between, you'll jump to the next frame in that direction.

To install framemove on its own:

(require 'framemove)
(framemove-default-keybindings) ;; default prefix is Meta

But you might want to use this in conjunction with windmove, in which case this is the integration code to add to your .emacs:

(require 'framemove)
(setq framemove-hook-into-windmove t)

With the integration with windmove, you just do SHIFT-right to move focus to the window to the right of the current, and when there are no more, focus will shift to the frame to the right.

Switching frames is a little more challenging than switching windows - mostly because windows are simply rectangular divisions of a window - it is easy to tell what window (if any) is to the right of the point. For frames, the answer is ambiguous because the placements of frames is free-form, and frames can overlap. I think this initial implementation does a reasonable job of choosing which frame to select. It first tries the frame directly in line with the point, and if one doesn't exist, it then uses the distance between the point (projected onto the edge of the current frame) and the frames in the given direction, and after that it just tries any frame that extends beyond the current frame in that direction.

Check it out.

Currently, when focus shifts to a new frame - the point does not change inside the frame.

Thursday, February 11, 2010

Emacs Tip #34: line-beginning-position, line-end-position

Such a simple thing - finding the position of the beginning or end of the current line. I've been completely oblivious to the existence of the functions line-beginning-position and line-end-position.

They return the position of the beginning or end of the current line.
They both accept an optional argument, which has the same
meaning as the argument to beginning-of-line or end-of-line.

They were first introduced in Emacs 20, in the middle of 2006. Which means I've been unnecessarily typing this for 4 years: