2009 in Review (belated)

Well, better late than never, I suppose.

Things in software/computing that were new for me last year:

  • I started writing Zeya, at first just to scratch an itch. I was surprised by the response to it.
  • My day job is now mostly writing Java. I'm more comfortable with more dynamic languages but there are quite a few things I like about Java. More about this later.
  • Switched to Google Chrome. It's blazing fast.
  • Started using Google Wave for a few things. It's still in a state of major flux and a lot of issues are still being ironed out, but I really like it. No other discussion tool I've ever used matches Wave in immediacy while scaling up to support readable and usable conversations that contain up to hundreds of contributors. In many scenarios I think having people on a Wave can make them more productive than if you could put them in a room together.

These were the most popular blog posts I wrote last year:

And these were the most visited blog posts that were written in previous years:

"Edit with Emacs" Chrome extension

Alex Bennée has written an Edit with Emacs extension for Google Chrome. It's Chrome's answer to Firefox's "It's all text" extension, which makes composing emails, blog posts, and other long-form text in a browser a lot more tolerable. (Hooray!)

Since Chrome extensions can't spawn arbitrary processes, the Edit with Emacs extension requires the cooperation of an additional edit server that can. The edit server is implemented in elisp and is bundled with the extension.

Installation instructions:

  1. Visit the extension gallery page and click "Install".
  2. In Chrome, click on the wrench menu, Extensions, "Options" under "Edit with Emacs", and follow the instructions there for setting up the edit server.
  3. After configuring the edit server, click "Test Edit Server" to make sure everything is working.
  4. Henceforth, in any new tabs you open, you'll see a little "edit" button next to any textarea elements, which you can click on to pop up a new editor frame. (You can also double-click on the textarea.)

Firefox 3.6

Mozilla released Firefox 3.6 last week. There are many noticeable improvements compared to Firefox 3.5, but two that stand out to me.

First, performance has improved quite a bit. And not just Javascript performance. Startup time is improved; the awesomebar responds instantly to keystrokes; Javascript and redraw/redisplay are faster. I no longer perceive a huge speed difference between Firefox and Google Chrome on most web apps, and the UI has become more responsive. (A few things—startup time, redraw/redisplay/scrolling, and creating/closing new windows and tabs—are still minorly annoying when I use Firefox alongside Chrome—but 3.5 and 3.6 are like night and day.)

Second, Firefox 3.6's implementation of HTML5 media, or at least audio, is superb. Having spent lots of time hacking on and listening to Zeya on Chrome, Firefox 3.5, and Firefox 3.6, the latter is the one where things most often just seem to work as spec'd. Moreover it's Firefox 3.6 (and only Firefox 3.6) that can consistently maintain the illusion that I'm using a native media player rather than a web-based one. That's colossal.

So, kudos to the Firefox developers.

PSA: the ubuntu-mozilla-daily PPA has nightly builds of Firefox 3.5, 3.6, and 3.7 packaged for all Ubuntu releases since Hardy. It's a low-hassle way to teach an old dog new tricks.

Two Python curiosities

I learn something new every day.

First, the sort of thing you could do to mess with someone when they step away from their Python interactive shell to go use the restroom:

>>> False = True
>>> False
True
>>> if False: print "Woo!"
... 
Woo!

Sure enough, True and False are just names in the builtin namespace that can be rebound. This capability seems pretty dangerous, so I'd expect to see something like it in C or in Lisp, but its presence in Python surprised me a bit.

Second, the following puzzle stumped me for a while when I first saw it. Why does the following expression evaluate as it does?

>>> "string" in [] == False
False

It's not a precedence issue, because that result is not consistent with either of the parenthesizations:

>>> ("string" in []) == False
True
>>> "string" in ([] == False)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: argument of type 'bool' is not iterable

It's actually a consequence of Python's support for chained comparison operators. That is, just as

5 < a <= 7

desugars to

5 < a and a <= 7,

the first expression above desugars to "string" in [] and [] == False, which is False.

It's good that the comparison operators (< <= > >= == != in is) work in this consistent way, but it can trip up Python novices who have not yet learned that they really have to write the more Pythonic expression "string" not in [] if they want to stay out of trouble.

Zeya 0.4

I'm pleased to announce the 0.4 release of Zeya. Zeya is a web server that lets you listen to your music collection, from anywhere, using nothing more than Firefox or Google Chrome.

Major changes since Zeya 0.3:

  • Gapless playback. This feature works with varying degrees of success on various browsers; I recommend Firefox 3.6. Firefox 3.6 > Google Chrome 4.x > Firefox 3.5.
  • "Shuffle" feature. (Thanks to Amit Saha)
  • "Repeat playlist" feature.
  • New playlist backend, which serves all the songs specified in an M3U playlist. (Amit Saha)
  • A sample Upstart script, which shows how to run Zeya at startup on Upstart-based systems, such as Ubuntu 9.10/Karmic and later. (Amit Saha)
  • Turned on traffic shaping for Google Chrome clients, so streaming doesn't hose low-bandwidth connections.
  • Other bug fixes and improvements to the listening experience.

Zeya has also now been packaged for Ubuntu! (More precisely, the Debian package has been synced to Ubuntu Lucid.) On Debian testing or unstable and Ubuntu 10.04/Lucid, install the zeya package:

# apt-get install zeya

(It may be several more days before 0.4 appears in Debian testing or Ubuntu.)

See http://web.psung.name/zeya/ for more information about Zeya, installation, getting started, reporting bugs, and development.

Charitable donations

I donated to the following charities and organizations in the past year (although I really meant to get this post out before the end of 2009):

UNICEF, to support relief operations following the earthquake in Haiti.

The Free Software Foundation, which has a simple but lofty goal: to ensure that the computers we use and carry around with us every day are actually accountable to us. Their public awareness efforts are invaluable, but if they did nothing but maintain the GNU operating system (including, ahem, Emacs), I would still be thankful beyond measure.

Wikipedia, for being one of the things that makes the web so wonderful.

Creative Commons, for showing that we can do better than "All rights reserved."

MIT Open CourseWare, for disseminating knowledge. OCW relies on donations to keep the lights on and to fund digitization of more classes.

My local branch of Feeding America (aka Second Harvest Food Bank). There is something depraved about hunger in the midst of plenty, and so close to home.

Amnesty International, that everyone may have the basic human dignities that you and I enjoy.

Subtleties of search-and-replace in Emacs

The search/replace mechanism in Emacs is a beautiful thing, full of subtleties.

It is pretty clear that its design has been honed to an edge by about 20 years of heavy use. You might not think there could be much to it, but a lot of apps that have come along since then could learn something from some of its features:

Incremental search. (isearch, or "find as you type") This has gained traction elsewhere in recent years. To search, press C-s and start typing; C-s again takes you to successive matches. Emacs highlights all the matches that are visible:

You can save quite a bit of typing this way because you can stop as soon as you've typed a prefix that uniquely identifies the text you're looking for.

In addition, the search UI is in the minibuffer, instead of in a separate dialog that covers part of your document.

Firefox, Chrome, and gedit all have fairly similar features; however, they only expose bare-bones search functionality through them. OpenOffice and most word processors bring up a big and jarring dialog.

Yanking from the document. This is like auto-completion for your searches. Suppose you're looking for all instances of "tag.artist" in your document. Press C-s and start typing...

Now you notice that the word following the highlighted match is exactly what you were looking for. Just press C-w to pull the next word into your query:

Then pressing C-s searches for subsequent occurrences of tag.artist. If you think this is useful, just think about how awesome it is when you need to search for superLongIdentifierNames in Java code.

You can also yank just the next character from the document, or everything up to the end of the line. Type C-h k C-s in Emacs to learn more.

Case replacement. If you have the text

Eat foo for breakfast!
Foo is the best medicine.
VOTE FOO FOR PRESIDENT

and replace (M-x query-replace or M-%) "foo" with "bar", what do you get? Exactly what you expect:

Eat bar for breakfast!
Bar is the best medicine.
VOTE BAR FOR PRESIDENT

This is clearly what you want most of the time, at least when you're editing prose. Gedit and OpenOffice don't do this (and Chrome and Firefox don't have a "replace" feature).

(What happens, you ask, if you're editing code, or anything else where case sensitivity is critical? Emacs's heuristic is to only turn on these smart replacements when your query and replacement text are both all lowercase. You can also manually toggle case-sensitivity in searching with M-c. For what it's worth, I've never noticed a false positive when editing code.)

Cycling behavior. Many programs do a poor job of letting you know when your search has wrapped around to the beginning of the document. In Firefox, I often find that I can inadvertently cycle through looking at all the search results two or three times before I realize what I've done.

After the last match in a document, Gedit and Chrome wrap around to the beginning silently. Firefox wraps around and concurrently displays a notification in the search UI. The problem is that if your attention is focused on the document content—as it probably is if you're scanning the individual matches—it's easy to miss the cues that tell you the search has wrapped around (e.g. Firefox's notification, or the scroll bar jumping back). You can end up back at the beginning of the document without realizing it.

At the other end of the spectrum, OpenOffice brings up a big and jarring dialog asking you "Yes/No" whether you want to continue searching at the beginning. Not so great, either.

How does Emacs deal with this? When you've reached the last match for a query in the buffer, pressing C-s does nothing, except raising a quiet notification that there are no remaining matches:

If you press C-s again, however, the search wraps back around to the beginning of the buffer.

That is, at the last match, C-s is not initially met with an immediate jump to a new match (as it usually is); Emacs gives just enough "pushback" to ensure that the user is aware when the search has wrapped, even if they're not paying any attention to the application chrome (minibuffer and scroll bars)! Very cool.

Displaying failed searches. When you type something that doesn't have a match, Emacs brings you to the maximal matching prefix of whatever you typed, and highlights the part of your query that wasn't found:

This kind of feedback gives you a good idea of whether or not you made a typo/error in your search: if you did, it often appears near the point where the text stopped matching.

If you only get all-or-nothing feedback telling you "no matches were found," then you are sort of groping around in the dark as you try to figure out what went wrong.

Search as a method for navigation. Perhaps the killer feature of isearch in Emacs is that you can use it as the primary method for moving around in a document, essentially replacing most of the functions of the PgUp/PgDn keys and the scroll bar in other applications.

Part of this has to do with the fact that the search UI is so lightweight (no dialog boxes!). However, isearch in Emacs is also cool because it facilitates many use cases:

  • During a search, you can press C-g to cancel and return to where you started. This is handy if you just wanted to search for something in order to get a quick glance at it.
  • …However, if you do indeed want to stop at the current match, press RET. Perhaps you want to make some quick edits or do some more extensive reading at that spot. But when you're done with that, you can press C-x C-x to return to where you originally were, because Emacs has set the mark to the place where you started.
  • …Or, you can just continue editing there with no intention of returning to your original position.

Gedit and OpenOffice really only support that very last use case. You can hold your existing spot in a document with the cursor, but then you're limited to looking around with the scrollbars, which is cumbersome. Or you can use the Find feature or otherwise move the cursor, but then you've lost your original position.

Emacs is like your very own personal assistant: it remembers things so you don't have to.

Conclusion

I haven't mentioned many of the miscellaneous keyboard-accessible commands and options available from within isearch and query-replace. These are well worth your time to learn, although they are quite self-explanatory if you read the integrated help:

  • For options available in isearch, type C-h k C-s.
  • For options available in query-replace, type C-h at a query-replace prompt.

I'd sum up some of the more generalizable design principles behind search-and-replace in Emacs as follows:

  • Reduce extraneous typing.
  • Stay out of the user's way whenever possible.
  • However, alert the user with immediate and useful feedback whenever necessary.