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

Sourdough Bread

April 28, 2016

I lost this link earlier today, so here it is in case I need to find it again: Sourdough Bread: How To Begin.

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

Arabic: A language with too many armies and navies?

June 24, 2013

Seen on Language Log: a piece in the Economist about Arabic:

Today, the Arab world is sometimes compared to medieval Europe, when classical Latin was still the only "real" language most people wrote and studied in—but "Latin" in the mouths of its speakers had become early French, Spanish, Portuguese and so on. Today, we recognize that French and Portuguese are different languages—but Arabs are not often sure (and are sometimes at odds) about how to describe "Arabic" today. The plain fact is that a rural Moroccan and a rural Iraqi cannot have a conversation and reliably understand each other. An urban Algerian and an urban Jordanian would struggle to speak to each other, but would usually find ways to cope, with a heavy dose of formal standard Arabic used to smooth out misunderstandings. They will sometimes use well-known dialects, especially Egyptian (spread through television and radio), to fill in gaps.

Short read, well worth your time. Also related: the ongoing discussion of whether Chinese is a language with dialects, or a language group with languages.

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

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

Sam Shapiro — Saving Ghandiah

April 30, 2010

I just learned of the passing, last summer, of Sam Shapiro, half of the dynamic duo behind Force Monkeys. His family has been erecting memorials: and Saving Ghandiah (Ghandaiah being his best-known handle). Here is some of his writing:






A lot of his art is really stunning. Here is one that I’m fond of. Look at the eyes in particular.

Comments Off

Emacs Got Git

April 29, 2010

I saw Emacs Got Git, sometimes called "Egg", listed on the list of git-related software on the git wiki (although now taken down) and of course on the EmacsWiki. I decided I’d give it a whirl. The most recent version I could find was the one listed on the EmacsWiki, the version from bplayer, which seems to still be actively developed. The incumbent here is either magit, which is by far the best user interface I’ve ever seen for any version control anywhere, or VC mode, which was written once to support SCCS and has largely survived unchanged since then.

A brief digression about magit and vc-mode. Magit is a little bit of a challenge to pick up: you actually have to read the manual. But the short version is: M-x magit-status to open a view of your repository, and then TAB things open and closed. You can press "s" to stage files, hunks, or even "highlight" lines using the region and stage only those. "u" to unstage; "c" to start a commit, and then C-c C-c to make the commit. You can create a commit that amends the previous commit by pressing C-c C-a in the log message buffer. It probably offends some that there are already conventions here for VC system integration, notably vc-mode. But vc-mode takes a file-based view of version control, has no support for staging hunks, and in general just doesn’t feel good to use. magit is much better — so much better that it is easily worth the break in convention.

So I thought I’d check out Emacs Got Git, to see if it was any better than magit. This isn’t a detailed analysis — actually I’ve probably spent longer writing this post than I did looking at Egg.

I find a screenshot is worth a thousand words. On my ~/etc repository, magit looks like this:


Magit highlights all the important details: which files are changed? Which are untracked? What commits exist locally that don’t exist on the remote?

On the same repo, Egg looks like this:


This is what we call the "angry fruit salad" school of UI design. Also, it doesn’t have a section for "unpushed" commits.

I’m going to be sticking with magit for the forseeable future.

Comments Off