Designing a right prompt

May 7, 2016

[Edit: the below is something I wrote when I was trying to figure out what I should implement in my fish shell. Then I spent about eight hours implementing it, and when I was done, I immediately found that there were things about it that I didn't like the way they "felt", so I changed them and edited the article to match. But experience shows that if there were already things that I didn't like before I tried them, there will probably be more things that I discover I don't like as I use them. So maybe it's best to consider this article less like a design guide and more like an exploration of the design space.]

Since I’ve started playing with the fish shell, I’ve started to mess around with my prompt so I can maintain the only feature I actually cared about from my bash configs, which is that it showed git status in the prompt. Because I’m vulnerable to nerd sniping, this made me start to think about what information I definitively wanted in my prompt and where (since fish offers a "right prompt" as well as the typical left one).

I found a bunch of examples online where people had customized their prompts to whatever they found appealing for whatever reason, but relatively little about what brought them to that design in the first place. What things did you put in what order in what locations and why? What things did you decide that you didn’t need and how does that affect your workflow?

This is a brief post about what my decisions were and why.

A prompt should balance three concerns:

  • Display important information.
  • Take up as little space as possible.
  • Be aesthetically appealing.

These concerns are in tension, because as you display more information, you take up more space, and as you condense things into a smaller space, you sacrifice aesthetics. I cared less about aesthetics than about space, so I decided I would try to keep my prompt to one line if possible, even if that meant an uglier prompt.

Which information is important? On most machines I use, the "default" prompt includes

  • my username
  • current hostname
  • current path
  • some kind of symbol, traditionally "$"

Sometimes tools like virtualenv will add a prefix to indicate what environment you’re in. I wanted to subsume that too.

I knew I wanted to add

  • git status when it makes sense — what branch, whether the working directory is clean, and if we’re in the middle of a some operation like merge or rebase. I do most of my git stuff from magit, but sometimes I do one-off commands from the shell.
  • a "warning" that would indicate if the last command took too long (inspired by fish’s built-in $CMD_DURATION variable)
  • a "warning" that would indicate if the last command failed

I decided that in order to maximize space, I was going to remove my username from the prompt — I usually only have one account on any given machine, and I use sudo when I need to execute commands as other users. I also didn’t need a symbol if there was some other clear way to distinguish where my prompt began (for example, if it were a different color, or if some other part of the prompt were distinctive).

Which things would go on the left, and which on the right? As far as I can tell, there are two factors that go into this decision:

  • Left prompts are more visible, since you look at what you type and your typing begins just after the prompt. Because of this, it makes sense to put things in the right prompt that you need but only occasionally.
  • Right prompts go away when your cursor gets to them, which frees up more space at the cost of dropping information. Because of this, it makes sense to put things in the left prompt that you may want to refer back to in a few lines.

I noticed that commands that change the git branch are usually pretty short, so I could put the git branch on the right and if I didn’t see it, I could assume it hadn’t changed.

On the other hand, if I were in the middle of a merge or rebase, I wanted that to be obvious, since (unlike most git operations) cleaning up after a mistake during an interrupted merge or rebase can be a pain. I thought the "command failed" warning could be condensed to a single character, and I thought I’d want to know every time a command failed, so that would go on the left too. And because I wanted last command status on the left, I should put $CMD_DURATION on the left too for consistency.

That left the hostname and path. My normal workflow involves opening a bunch of tabs, one for each "location", and changing rarely. As a result, during the normal course of my day, I usually know "where I am", and in modern shells, I can just glance at the title bar if I forget where a tab is. But both hostname and path are super useful to refer back to during a transcript or especially when something goes wrong. For this reason I thought I would experiment with removing hostname and path from my prompt entirely but emphasizing changes to those values so that they would show up more obviously in a transcript. fish doesn’t provide a hook for this, but it’s easy enough to simulate by tracking state in a global variable and checking if it changed. I wasn’t quite ready to get rid of it entirely, but for now, the cwd is in the right prompt. We’ll see how that goes.

It took me a while to discover that fish ships with extensive support for putting git in your prompt (with the builtin __fish_git_prompt). It’s not quite as flexible as you might want, but it does have a bunch of sample code that might be useful if you want some of its power.

You can see what I ended up with at fish_prompt, fish_right_prompt, and fish_title. Hope this helps someone else designing their prompt!

Comments Off

The fish shell

April 7, 2016

Continuing in the vein of my previous posts about changes I’m making to my development setup, this week I have been looking at shells. Somewhere once upon a time I encountered the idea that cool kids don’t use bash, and the commonly-cited alternatives are zsh and fish. A thread on Reddit pointed out a few of the compelling advantages of zsh over bash, and Ars Technica has a fairly old article detailing some of the ideas behind fish.

Some people complain about fish’s not supporting !! and related syntax like !$, but I’ve always found the expansion of ! problematic (when e.g. writing commit messages) and I prefer to just use interactive editing of commands from my history, or Alt-. to retrieve the last word in a previous command. (fish supports both quite well.)

Some features that I really like about fish so far are:

  • Tab completion. I think I must have tried zsh once a long time ago because I have some sort of vestigal memory of trying to tab-complete some filename and hating it. Specifically, I remember hitting Tab a few times (which is deeply ingrained in my muscle memory from years of using bash), and zsh immediately inserting something into my command line, with my only option to keep pressing Tab until I got to what I wanted. (I believe this is called menu completion in the literature.) I felt this was too aggressive; if I wanted to refine the filename a bit, or if I saw that I had entered the wrong directory, I had to erase what the shell had just injected. fish’s tab completion is like all the best parts of this mechanism, but better. It isn’t adequately covered in the documentation, but this Stack Overflow post does a good job detailing its behavior. Basically, you can cancel the tab completion using Escape, and you can refine the search by typing stuff even once you are in "menu" mode, sort of like an ido/swiper sort of thing. Very slick.
  • If a command prints some output that doesn’t end in a newline before you get to a prompt, fish puts out a cute symbol and inserts a newline. Your prompt always starts on a new line, but you get a clear indication that what you see isn’t exactly what you got.
  • Ctrl-K and Ctrl-Y on the fish command line interoperate seamlessly with my windowing system’s clipboard.
  • I’ve never been super into pushd/popd, but fish has prevd and nextd which seem pretty nice.

Lots of people online recommend oh-my-zsh or zprezto, but it mostly seems like the things zsh offers (even with these packages) aren’t incredibly amazing. Maybe there’s some draw for die-hards, but there’s a lot of overhead involved in learning these packages, setting them up, and maintaining them. By comparison, fish has hardly any configuration available at all, so hopefully you like how it works, because if you don’t, you can’t change it.

In general, the dichotomy between zsh and fish is between the incredibly flexible Swiss-Army-knife all-singing-all-dancing tool with a gazillion options, and the beautifully crafted tool with only one, extremely-carefully-thought out option. zsh is KDE and fish is GNOME. Or, zsh is Perl, and fish is Python. I’m sure you can come up with some other analogies.

I’m still getting my prompt and favorite functions set up, but a few commands I’ve found useful:

  • type [commandname] lets you see what kind of command something is, and if it’s a shell function, see its definition. Apparently this is common to even bash, but I had never used it before.
  • vared lets you interactively edit a variable. zsh has this too; fish also has funced for editing functions.

A couple things I’m annoyed about:

  • The fish web page compares fish’s autocomplete behavior to a browser’s, but there’s no way to tell fish to not record history for a shell session, analogous to a browser’s "private browsing" mode.
  • In both fish and zsh, array indices start at 1, not 0 as you might be used to.
  • fish’s readline doesn’t support Control-_ for undo, and maybe doesn’t support undo at all.
Comments Off

All the disabled commands in Emacs

March 13, 2016

I’m still cleaning up my emacs configuration. I stumbled across the configuration I did to enable the narrow-to-region command, and figuring that the "disabled" commands are likely to be pretty interesting, wondered "What other commands are ‘disabled’?" There’s no list that I could find online, but you can find out for yourself with a little lisp:

  (lambda (x)
    (when (get x 'disabled)
      (message "disabled: %s" x))))

On my machine, this produces the list:

disabled: erase-buffer
disabled: set-goal-column
disabled: narrow-to-page
disabled: upcase-region
disabled: narrow-to-defun
disabled: scroll-left
disabled: Info-edit
disabled: dired-find-alternate-file
disabled: downcase-region

… as well as, of course, narrow-to-region, which I de-disabled myself.

Comments Off

org-mode and use-package

March 12, 2016

I just spent a bunch of time converting my old, crufty, el-get/elhome dot-emacs.d files to the new hotness, use-package. I spent more time than I’d like to admit to get org-mode to work correctly. I needed to both remove it from the load-path as well as deleting org from the package--builtins list. The full solution is available here if you want to use it.

Comments Off


January 27, 2013

To be completely honest, I’m a little disappointed with ZaReason‘s tablet offering, the ZaTab (N.B. currently sold out while they develop the successor). And I feel guilty about saying so, because I love ZaReason and I want them to succeed.

Here’s the basic problems I’ve been having with my device.

  • Device doesn’t charge. This is kind of a big problem for a device to have. It’s kind of a qualified "doesn’t charge", though, because if you plug the USB cable in just the right position, it will charge, but slowly (36 hours for a charge?). It seems like the connectors aren’t well-seated or maybe the case is coming loose, or maybe the connectors are breaking off the motherboard. Who knows? I wrote support an email on the 14th of December. I thought I’d cut them some slack for the holidays, but it’s been almost three weeks without any response — even so much as a "sorry, we can’t help you, it’s almost certainly completely fucked and it’s out of warranty". (I pinged them on Twitter too; nothing.)
  • When a MicroSD card is inserted, the device becomes much more sluggish and seems to run out of charge a lot faster. Maybe this is related to the above, maybe not. This was with a 32GB card; I haven’t tried anything else since them.
  • No clear way to update device. Maybe this is my fault for factory-resetting the device, but it’s running CyanogenMod 9, right? So why can’t I just easily install Cyanogen 10 to see if the above problem goes away? I don’t want to dick with flashing ROMs and installing the Android SDK. That’s the whole reason I bought a free device!
  • Oh, a standard micro-HDMI connector doesn’t quite fit into the port. You can order a cable with the tablet that has a micro-HDMI connector that’s an itsy bitsy bit longer than standard, which works. Yuck.

I don’t want to jump to any conclusions as far as what this means about ZaReason’s technical chops or their business practices or whatever. Maybe I just got a bad one, or I was too rough on it, or it got banged up in shipping and it’s no one’s fault. But when I buy devices, I want them to just make my life unilaterally and unequivocally better. Instead it seems like nobody ever sells exactly the product I really want, and ZaReason just manages to come a little closer than everyone else.

I guess the good news is that in the last few years, open hardware has suddenly gotten a lot more plausible. Please be aware of PengPod and Jolla. It’s a brave new world..

Comments Off

Dr Nic’s 8 steps for fixing other people’s code

January 27, 2013

A clearly-articulated if somewhat Ruby-centric and dated ("now do svn diff") explanation of how to contribute a patch to an open source project.

  1. Snoop around

Load up the project into your editor and poke around.

If it has test cases (!) try running them. You may need to create some databases and/or modify the Rakefile/connection details to work with your database. Hopefully the project comes with rake tasks to create/drop the databases. rake -T should find them.

Comments Off

Upgrading an uncooperative ReadyNAS

August 31, 2012

My ReadyNAS was refusing to be upgraded from version 4.1.6. Every time I tried to induce it to get a new upgrade, it complained that the checksum had failed. I eventually got it to upgrade by following the instructions on this page, which seems to avoid doing a checksum. I’m not sure if it will be necessary to repeat the process for every new release..

Comments Off

HOWTO write a Perl Obfu

September 5, 2011

Here’s one I found once a long time ago and keep thinking of randomly. jynx on Perlmonks explains what makes and breaks an Obfuscated Code entry. Does anyone still write Perl? Does anyone still write (intentionally) obfuscated Perl? Still, I really like the way he offers examples and counter-examples of each principle.

2) pack/unpack is not obfuscation

The reason i list the counter-example is because it is not unpacking anything like what you think at first glance. While the obfu itself does need some work, that is an acceptable use of unpack. On the other hand, looking at the example we see a fairly common use of unpack: get the string and unpack it, oh look the string is japh. While a packed string is line noise, it’s easy to see past it and note what the code is doing if it’s a simple obfu.

Comments Off


September 4, 2011

Since I have a tag dedicated to version control, I thought to use it to link to iolaus, a git porcelain that emulates darcs. It’s written by the same guy who wrote darcs, possibly as a one-off project. Perhaps it was meant to solve the darcs "exponential merge time" problem. The same problem has been drastically improved in Darcs 2, and iolaus hasn’t been updated since March 2010, so it probably isn’t worth worrying a lot about.

I realized that the semantics of git are actually not nearly so far from those of darcs as I had previously thought. In particular, if we view each commit as describing a patch in its "primitive context" (to use darcs-speak), then there is basically a one-to-one mapping from darcs’ semantics to a git repository. The catch is that it must be a git repository with multiple heads!

Fortunately, this is not such a foreign concept to git. In fact, git has a whole framework to help users manage repositories with multiple heads (see, e.g. checkout and branch). So it’s not so very foreign at all. There are just a couple of major differences how git works. First, in git your working directory will only reflect one of the heads, while in darcs (or iolaus) the working directory reflects the union of all changes in the repository.

Just to make things even more interesting, it’s written in Go.

Comments Off

SSH on the N900

May 24, 2010

I spent a few hours today dicking with my N900 and thought I’d write up some of the things I dealt with.

For a long time I’ve been using Dropbear SSH client/server on my phone, due to an alleged less-memory-usage. (When your phone starts swapping, it sucks big time.) Dropbear even supports serving SCP, but does not support SFTP. This prevents you from using any relatively-nice "file transfer over SSH" GUI, such as Nautilus’s "ssh" support or gFTP. (I think Konqueror’s fish mechanism would still work, but that is of limited utility to me right now.) It may be possible to use the sftp from OpenSSH with dropbear, but since the Dropbear packages conflict with the OpenSSH packages in the Maemo repository, that’s not especially on an N900. In fact, dropbear-scp conflicts with openssh-common (both provide /usr/bin/scp, which I think is silly, but there you are).

Of course, if you insist on using Dropbear, you can use Bluetooth to copy files over Obexftp (which Nautilus supports nicely). But since this requires Bluetooth hardware to be powered on both the laptop and the phone, I decided to replace Dropbear with OpenSSH.

Installing OpenSSH server on your N900 forces you to change your root password (the default is "rootme"), whether you’ve already changed it or not. Kind of annoying. The user account by default "doesn’t have a password", which I think means all password access is disabled. Folk wisdom suggests that giving a password to the user account "could" cause problems, but I think this is based on an (incorrect) belief that the default password is "blank" (in fact, it’s invalid, meaning there is no phone software that relies on using a password to switch to the user account, so there should be no problem with granting a password). Nevertheless I decided to just drop in a key using authorized_keys. But if you don’t set a password, OpenSSH won’t let you log in (even using publickey access); the log messages will tell you that your account is "locked". The reason is that OpenSSH looks at /etc/passwd to decide whether to let you in using any access methods at all; since the password hash is "!", it locks you out.

This page shows how to fix the "locked account" status.

Comments Off