Showing posts with label emacs-advanced. Show all posts
Showing posts with label emacs-advanced. Show all posts

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))
ediff-do-hexl-diff)
(condition-case err
(progn
ad-do-it)
(error
(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
(progn
ad-do-it)
(error
(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"
(buffer-substring-no-properties
(line-beginning-position)
(line-end-position)))
(y-or-n-p "The binary files differ, look at the differences in hexl-mode? ")))
(error (error-message-string err)))))

Wednesday, September 30, 2009

Emacs Tip #33: paredit

I recently found paredit. It is a mode which allows you to edit s-expressions (sexps) in a structured way.

Essentially it forces you to maintain balanced parentheses by always inserting/deleting matched pairs. It is very easy to modify the current sexp to: enclose (wrap) the next element, to merge it up one level (aka splice, i.e. remove a set of parentheses), to split the current sexp, etc.

It works with round parens (), square brackets [], curly braces {}, and angle braces <>. It also handles string quotes " the same way.

Deleting and killing of text also works nicely. For example, C-k is bound to 'paredit-kill which has the following behavior (note: | represents the cursor position):

(foo bar)|     ; Useless comment!
->
(foo bar)|

(|foo bar) ; Useful comment!
->
(|) ; Useful comment!

|(foo bar) ; Useless line!
->
|

(foo "|bar baz"
quux)
->
(foo "|"
quux)
It's taking a little getting used to, but I'm finding myself thinking less about where the current block of code ends than I used to. The parentheses just take care of themselves...

This is how I enabled it:


(defun turn-on-paredit-mode ()
(paredit-mode +1))

;; I didn't like C-j being overridden, I want it to still work the old way in lisp-interaction
(eval-after-load "paredit"
'(progn
(define-key paredit-mode-map (read-kbd-macro "C-j") nil)))

(add-hook 'emacs-lisp-mode-hook 'turn-on-paredit-mode)

The cheat sheet is probably the best way to get an idea of what is possible. Check it out here. Also, the command documentation is very complete (see the above which was grabbed right from the C-k documentation, so it's easy to remind yourself what is possible.

About the only problem I've had is when I've yanked code that has unbalanced parens. So I wrote a little advice which at least warns you and allows you to abort if the yanked text is unbalanced. It's not perfect, but it is a reasonable first implementation:

(defun paredit-check-region-for-yank ()
"run after yank and yank-pop to verify balanced parens"
(when paredit-mode
(save-excursion
(save-restriction
(narrow-to-region (point) (mark))
(condition-case nil
(check-parens)
(error
(if (not (y-or-n-p "The text inserted has unbalanced parentheses, continue? "))
(delete-region (point-min) (point-max)))))))))

(defadvice yank (after yank-check-parens activate)
(paredit-check-region-for-yank))
(defadvice yank-pop (after yank-pop-check-parens activate)
(paredit-check-region-for-yank))

Tuesday, March 3, 2009

More Advice

Way back when I wrote a tip about Emacs Lisp's advice.

How I wish other languages had this capability. A couple of questions came up recently on stackoverflow.com that were (imo) best answered using advice. So, if you're struggling to see when to use advice, I think they're pretty good examples.

"Diff, save or kill" when killing buffers in emacs

Can I change emacs find-file history?

Monday, April 21, 2008

Emacs Tip #18: Keyboard Macros

If you find yourself doing a repetitive set of keystrokes, but don't want to bother with learning how to actually write a script for that, then record a keyboard macro.


C-x ( begins recording
C-x ) ends recording
C-x e executes the last keyboard macro
C-x C-k lets you edit the keyboard macro you just wrote

If find yourself using lots of macros, you can even save them to your
.emacs and name them (for later use).

For more documentation, see the info page:

C-h K C-x (

Or check out the Keyboard Macros section in the Emacs wiki.

Thursday, December 27, 2007

Emacs TIp #6: advice

This is an advanced tip, but it has simple applications.

Don't you wish you could change some of the built-in routines? You
like the way they work, but would tweak it just a little bit.

For example, 'ispell' does a great service: it provides suggestions
for the correct spelling of a misspelled word. Wonderful, I use it
all the time. However, it often has the order of choices exactly
opposite of what I'd expect.

e.g. `waht' - the correct option 'what' is #7!

`sopporting' - the correction option 'supporting' is #2!

It's much easier if the choice I usually pick is the first one.
So, it'd be great if I could just tell 'ispell' to reverse the order
of the choices provided.

You can!

Advice is a mechanism that lets you get code to run before, after, or
"around" existing elisp routines. You can modify the arguments going
in, you can modify the result, you can call the existing routing
multiple times. And this advice can be nested too.

Why use advice as opposed to re-writing the routine?

Because (especially in this example) - you don't want to muck with
having your own copy of the code. It's messy, and you might miss
upgrades to that routine with new versions of Emacs.

So, with this case in hand, I spent a minute tracking down the routine
that could be advised. I spent a minute writing the advice:

(defadvice ispell-command-loop (before ispell-reverse-miss-list activate)
"reverse the first argument to ispell-command-loop"
(ad-set-arg 0 (reverse (ad-get-arg 0))))


And now my spelling choices are in the right order! yay.

For more information on advice:
C-h i m advice RET