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))

Somewhat meta: blogs on reddit

Recently, doing a vanity search, I saw a link to this blog from reddit. I'm not a reddit user, but found it interesting that some of my posts had made it on there (as you can see here: trey-jackson.blogspot.com on reddit.com).

What I find curious about reddit as it applies to blogs is that it ends up taking the conversation away from the blog. This works well for reddit users, but ends up fracturing the audience. I don't get many comments (I have very few readers) and would have appreciated the reading the comments made on reddit.

For what it's worth, I did update a post to fix a coding bug noticed by someone on reddit (ironically, their fix had syntax problems at the same spot as well).

Tuesday, August 4, 2009

Emacs Tip #32: completion-ignore-case and Emacs 23

Everyone is all a-buzz over the new release of Emacs. I've started using it, and I do like the font support.

There was one surprise. I had the old setting

(setq completion-ignore-case t)

because I hate typing capital letters when I can avoid it. Enter Emacs 23.1, and I now have to type capital letters for buffer names and file names (which is 90% of what I use minibuffer completion for). It turns out, there are two new variables, one for each of the types of completion:

(setq read-buffer-completion-ignore-case t)
(setq read-file-name-completion-ignore-case t)


Everything else in the upgrade has been smooth.

Wednesday, June 24, 2009

Emacs Tip #31: kill-other-buffers-of-this-file-name

A friend mentioned he wanted a way to get rid of all the other buffers visiting files that of the same file name. I instantly realized this was something I'd wanted for a long time without knowing it.

My usage is that I'm often viewing different versions of the same file, usually in different sandboxes. And, I might also have older versions checked out, whose file names are the same, but end with ".~1.12~" (or whatever version number was checked out).

I'm still old-school and don't use anything fancy for my buffer switching like `ido` or `icicles` or `iswitchb` or any of the other myriad of choices. And, as a result, it's a pain to switch because I have to now discern between the variants. Well, do that once, type `C-x K`, and that'll be the only buffer left for that file name.


(global-set-key (kbd "C-x K") 'kill-other-buffers-of-this-file-name)
(defun kill-other-buffers-of-this-file-name (&optional buffer)
"Kill all other buffers visiting files of the same base name."
(interactive "bBuffer to make unique: ")
(setq buffer (if buffer (get-buffer buffer) (current-buffer)))
(cond ((buffer-file-name buffer)
(let ((name (file-name-nondirectory (buffer-file-name buffer))))
(loop for ob in (buffer-list)
do (if (and (not (eq ob buffer))
(buffer-file-name ob)
(let ((ob-file-name (file-name-nondirectory (buffer-file-name ob))))
(or (equal ob-file-name name)
(string-match (concat name "\\(\\.~.*\\)?~$") ob-file-name))) )
(kill-buffer ob)))))
((message "This buffer has no file name."))))

Edited Sept. 30, 2009 to fix coding error.

Friday, June 19, 2009

Searching GNU From Firefox

When writing up answers for Emacs related questions on stackoverflow.com, I like to include links to the info pages. I got tired of having to go to the gnu site and then do a google site-specific search. I tried looking for a Firefox search plugin that already did that (or something similar), but to no avail.

However, it's pretty easy to write the search plugin yourself. The main page for creating search plugins on mozilla's developer site gives you pretty much all you need to know, except for where to put the xml file. This page shows you that the installation dir is very likely C:\Program Files\Mozilla Firefox\searchplugins.

You can read the documentation yourself, or just check out the snippet of XML I wrote.

Probably the "hardest" part was finding an icon, and the all-knowing google led me to the www.favicon.cc web page, where someone had drawn the standard GNU icon and made it available under a Creative Commons license:

Friday, June 12, 2009

Emacs Tip #30: igrep

Finding text in files is an everyday (every hour) occurrence for most programmers. And in Unix-land, people generally use grep to do the searching.

Of course Emacs has an interface to grep, which now (as of Emacs 22) even has interfaces for using both find and grep together to search through directory structures (grep-find, rgrep).

But, before those existed, someone wrote a nice package igrep.el, which has a very intuitive (IMO) interface.

M-x igrep-find RET RET /search/in/here/*.cxx RET

which will search for regexp in all the files with the extension .cxx underneath the /search/in/here/ directory.

As you'd expect, the results of the search show up in a buffer, and clicking on any one of them jumps you to that spot in the file. And the standard M-x next-error (aka C-x `) will jump you to the next match.

About the only thing I don't like is that the paths returned in the results are the fully expanded path. In other words, when the paths to the files are really long, most of the result buffer is taken up with just the pathname. So I wrote this little snippet of code which post-processes the buffer to trim the names relative to the search path entered (in the example above, all the /search/in/here/ would be removed from the file names.


(defun my-shorten-filenames-in-compilation (buffer &optional stat)
"remove the absolute filenames if they match the default-directory"
(interactive "b")
(save-excursion
(set-buffer buffer)
(goto-char (point-min))
(let ((buffer-read-only nil)
(base-dir (if (re-search-forward "find \\([^ ]+\\) " nil t)
(if (string-match "/$" (match-string 1))
(match-string 1)
(concat (match-string 1) "/"))
default-directory)))
(setq default-directory base-dir)
(while (re-search-forward (concat "^" default-directory) nil t)
(replace-match "")))))

(add-hook 'compilation-finish-functions 'my-shorten-filenames-in-compilation)


I do realize that the built-in M-x rgrep does this automatically, I just prefer the igrep interface.

Tuesday, May 19, 2009

Emacs Tip #29: customizing hippe-expand

I read A Curious Progammer's post on customizing dabbrev - specifically to skip certain regular expressions at the front of words.

I remember customizing it for Tcl to avoid looking at the $ used to dereference variables. But, I no longer use dabbrev, I use the slightly more general hippie-expand. However, hippie-expand doesn't use the dabbrev settings.

So, the question is, can you get hippie-expand to do the same thing? The answer is, "yes" of course.

hippie-expand does things slightly differently - a little cleaner IMO. It uses syntax tables to determine what to skip. And, since syntax tables are set up appropriately for every mode, the skipping does what you want w/out having to potentially customize a regexp for each mode.

Syntax tables are Emacs' way of encoding the syntactic use of each character in a buffer, and this knowledge is used for motion and regular expressions. That's how forward-word has reasonable meaning in the various programming modes, text mode, shell mode, etc. Similarly, the syntax tables define list motion.

The syntax table has the basic character types: whitespace, punctuation, word, symbol, open/close parenthesis, string, comment start/end, etc. For example, the alpha-numeric characters are generally word constituents. In html-mode the <> symbols are matching parentheses, but in most other programming modes they are punctuation characters.

The variable you set is hippie-expand-dabbrev-as-symbol, which defaults to t. Basically, if non-nil, hippie-expand will try to expand the word under the point including symbol characters, which is not what I personally want. I want to only expand the word characters under my point, so I've set it to nil.

(setq hippie-expand-dabbrev-as-symbol nil)