Source for
git clone git://
Log | Files | Refs | README | LICENSE

commit edc69880480d09105208ff3cbb8713ccea65f6ed (patch)
parent fec9e6489e3669165a92a33675692409245cd0db
Author: Alex Karle <>
Date:   Thu,  1 Sep 2022 00:34:40 -0400

blog: Add post on Acme Git GUI

I'd like to work on the formatting a bit of the demo, but this is
post-ready I think (and I need sleep!).

Mwww/blog/index.txt | 1+
Awww/blog/plan9-acme-git-gui.txt | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 214 insertions(+), 0 deletions(-)

diff --git a/www/blog/index.txt b/www/blog/index.txt @@ -12,6 +12,7 @@ For an up to date list of software/hardware I use, see ## 2022 +- 08/22 [Using the Plan 9 Plumber to Turn Acme into a Git GUI](/blog/plan9-acme-git-gui.html) - 06/22 [Automating Multi-User WireGuard setup on OpenBSD](/blog/wireguard-management.html) - 05/22 [Starting a Tilde Community for Fun and the Learnings](/blog/starting-a-tilde.html) - 05/22 [Typesetting a Resume with `mandoc(1)`](/blog/mandoc-resume.html) diff --git a/www/blog/plan9-acme-git-gui.txt b/www/blog/plan9-acme-git-gui.txt @@ -0,0 +1,213 @@ +# Using the Plan 9 Plumber to Turn Acme into a Git GUI + +_Published: August 31, 2022_ + +## Backstory + +I first gave Acme a [somewhat serious spin](/blog/exploring-plan9.html) +~4 months ago and decided that the mouse-driven editor was a bit too +unwieldy coming from my keyboard focused tool suite. + + +However, a couple weeks ago I watched a snippet of the Lex Fridman podcast +where he [interviewed John Carmack]( +on IDEs (the tldr being that an IDE with a powerful debugger is crucial). +Carmack advocating for a heavy IDE naturally inspired me to revisit one +of the most spartan editors I've worked with: Acme. + +My previous post covered some of my first impressions and basics of what +Acme is, so in this post I wanted to do a deep dive into one of my favorite +features: the "look"/"plumb" behavior. To illustrate it, I'll walk through +how I was able to use it to turn Acme into a Git GUI. + +## The Power of Button 3 + +Button 3, or "right click", will "look" in Acme. More specifically, it +will take either the selected text (via Button 1) or the word under the +cursor and try to: + +1. Identify if it's a file/directory that exists, and open it in a buffer +2. Send the snippet to the `plumber(4)` +3. If all else fails, search for the word in the buffer + +Number (3) can be a great way to quickly click through usages of a +variable in a function, and (1) is super helpful in exploring the +filesystem and navigating to specific portions of files from tool +output, but (2) is where the magic begins. + + +The plumber gets its name from being a message bus between Plan 9 +programs. In a Plan 9 ecosystem, it appears (I haven't tested this) +that individual servers for things like "web" or "mail" run and +programs like Acme can interact with them by sending messages with +`plumb(1)` through the plumber. + +In practice on a UNIX-like system, I'm running very few servers +(just the plumber itself and `fontsrv(4)` for nicer fonts in Acme), +so the plumbing rules usually have a fallback option of running a +command (rather than passing it to a server). + +The plumber deciphers the messages from `plumb` by matching the +data it receives (in this case the text under our click) against a +series of patterns in a set of rules outlined in `plumb(7)`. By +adding your own rules, you can configure button 3 to open any +external program. Simple rules might match URLs and open a browser, +but given that Acme itself exposes effectively an API through the +filesystem, we can write little programs that in turn modify the +contents of Acme. + +## Plumbing Rules for Git + +In Git, all objects (primarily commits, blobs, and trees) have a +SHA, which is a hexidecimal string, making it easy to match on. Git +likes to shorten them in tool output, so a primitive regex to match +them might be: + + + [a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+ + +That's 6 or more hex digits in a row (an astute reader might warn +that this would also match, say, CSS color codes, but for our +purposes it'll work!). + +The full plumbing rule looks like so (in `~/lib/plumbing`): + + type is text + data matches '[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+' + plumb start rc -c 'git -C '$wdir' show '$0' >[2=1] | plumb -i -d edit -a ''action=showdata filename=/'$wdir'/'$0'''' + +We tell the plumber that if the type of the message is text, and if +it matches our pattern, that it should start the external program `rc(1)` +(the plan9 shell) with the command: + + git -C $wdir show $0 >[2=1] + +To run git in the working directory of the buffer the click was +made on and run `git-show(1)` on the full text that was matched +(with stderr duped to stdout). + +This command of course doesn't do much by itself--it will output +to stdout. So to make use of the output, we send it back to +`plumb(1)`, this time telling it to send to the `-d` (destination) +`edit` (editor) with the action of showing data. This will open a +new Acme buffer with the contents of the git object (whether it's +a commit, the file for the blob, etc). + + +## Going Full GUI + +At this point, we could run `git-log(1)` in a `win(1)` buffer (a +shell session in Acme) and start clicking SHAs to see the diffs, +but that's not super helpful. + +Looking at how I like to interact with Git in Vim, I commonly do +the following: + + +1. Look at the full git log, with ability to dive into commits +2. View the short git log for a specific file to see when things changed at a high level +3. View the `git-blame(1)` to see when things changed at a line-level + +The first can be solved by writing a small wrapper around git-log, `gl`: + + #!/bin/sh + # git-log, but piped through plumb so that it: + # 1. doesn't scroll to the end + # 2. has the proper "filename" so that plumbing SHA's works + git log "$@" | plumb -i -d edit -a "action=showdata filename=/$(pwd)/git-log" + +For a shortened version (easier to browse), we can simply call this +with `--oneline` (`glo`): + + + #!/bin/sh + exec gl --oneline "$@" + +The output on this repo looks like so: + + [/path/to/ Del Snarf Redo | Look] + 28a5f1c blog: Edit typos and small rephrasings for wggen post + 85b54e2 blog: Add post on wggen tool to manage wg creds + 6c6d8e0 blog: Add post about + da01dba blog: Add post about mandoc resume + ... + +Finally, we can get a per-file history by just filtering `glo` (`gv`): + + #!/bin/sh + # gv -- imitation of :GV! vim plugin + file=${1:-$samfile} + glo -- "$file" + +Here we see a nice trick: `gv` takes in an argument (so that we can +specify the file to log), but if it's empty it defaults to `$samfile`, +which is set by Acme to be the current buffer being edited. So +putting `gv` in the tag of the README and middle clicking to execute +gives the same output as before, but filtered! + + +Git blame uses the same concepts (`gbl`): + + #!/bin/sh + # git blame, in acme! + file=${1:-$samfile} + git blame --date="short" "$file" | plumb -i -d edit -a "action=showdata filename=$file:BLAME" + +Calling `gbl` on the README pops up a new buffer with the following: + + [/path/to/ Del Snarf Redo | Look] + 5e9783a6 (Alex Karle 2020-06-18 1) + 5e9783a6 (Alex Karle 2020-06-18 2) ============= + 016a5d70 (Alex Karle 2020-07-14 3) My small corner of the internet. + +This may seem unhelpful, but remember that clicking any of those SHA's will +open then in a new buffer with the full diff! Clicking 5e9783a6 gives: + + [/path/to/ Del Snarf Redo | Look] + commit 5e9783a6ce559f76da83b2530059c9664f43306e + Author: Alex Karle <email @ domain> + Date: Thu Jun 18 00:18:48 2020 -0400 + (snip) + diff \--git a/.gitignore b/.gitignore + new file mode 100644 + index 0000000..84c048a + \--- /dev/null + +++ b/.gitignore + @@ -0,0 +1 @@ + +/build/ + + +And what's this? More SHA's to click? 84c048a will give the full +contents of .gitignore at this commit (if the diff doesn't show +it). + +The most incredible bit is that Acme doesn't need to do much to +support this use case--the flexibility of choosing an external (and +extensible) program to match the text and the ability to spawn new +buffers in Acme via any language externally enables this beautifully. +Compare this to the [~500L of VimL in +vim-fugitive]( +to convert the Vim sidebar into an interactive blame viewer. (No +shade towards Tim Pope--if you're reading this, huge thanks for +everything you've ever written, it's inspired me and shaped my +career!). + + + +## Conclusion + +I'm really glad I chose to revisit Acme. While it's probably never +going to replace Vim in my toolkit, it was such an incredible +experience to be able to turn the tool into a pretty solid Git GUI +(minus syntax highlighting of course) in just about an hour or so +(including time to learn plumbing rules). + +The concept of a universal yet personally customizable plumber is +beyond cool, and the fact that Acme has one of the 3 buttons reserved +to plumb text allows for developers to easily hyperlink +plaintext, which is awesome. + +If you'd like to give it a spin, all my scripts (including my starter +script for Acme) are Open Source and live in my +[dotfiles]( +