plan9-acme-git-gui.txt (8143B) [raw]
1 # Using the Plan 9 Plumber to Turn Acme into a Git GUI 2 3 _Published: August 31, 2022_ 4 5 ## Backstory 6 7 I first gave Acme a [somewhat serious spin](/blog/exploring-plan9.html) 8 ~4 months ago and decided that the mouse-driven editor was a bit too 9 unwieldy coming from my keyboard focused tool suite. 10 11 12 However, a couple weeks ago I watched a snippet of the Lex Fridman podcast 13 where he [interviewed John Carmack](https://www.youtube.com/watch?v=tzr7hRXcwkw) 14 on IDEs (the tldr being that an IDE with a powerful debugger is crucial). 15 Carmack advocating for a heavy IDE naturally inspired me to revisit one 16 of the most spartan editors I've worked with: Acme. 17 18 My previous post covered some of my first impressions and basics of what 19 Acme is, so in this post I wanted to do a deep dive into one of my favorite 20 features: the "look"/"plumb" behavior. To illustrate it, I'll walk through 21 how I was able to use it to turn Acme into a Git GUI. 22 23 ## The Power of Button 3 24 25 Button 3, or "right click", will "look" in Acme. More specifically, it 26 will take either the selected text (via Button 1) or the word under the 27 cursor and try to: 28 29 1. Identify if it's a file/directory that exists, and open it in a buffer 30 2. Send the snippet to the `plumber(4)` 31 3. If all else fails, search for the word in the buffer 32 33 Number (3) can be a great way to quickly click through usages of a 34 variable in a function, and (1) is super helpful in exploring the 35 filesystem and navigating to specific portions of files from tool 36 output, but (2) is where the magic begins. 37 38 39 The plumber gets its name from being a message bus between Plan 9 40 programs. In a Plan 9 ecosystem, it appears (I haven't tested this) 41 that individual servers for things like "web" or "mail" run and 42 programs like Acme can interact with them by sending messages with 43 `plumb(1)` through the plumber. 44 45 In practice on a UNIX-like system, I'm running very few servers 46 (just the plumber itself and `fontsrv(4)` for nicer fonts in Acme), 47 so the plumbing rules usually have a fallback option of running a 48 command (rather than passing it to a server). 49 50 The plumber deciphers the messages from `plumb` by matching the 51 data it receives (in this case the text under our click) against a 52 series of patterns in a set of rules outlined in `plumb(7)`. By 53 adding your own rules, you can configure button 3 to open any 54 external program. Simple rules might match URLs and open a browser, 55 but given that Acme itself exposes effectively an API through the 56 filesystem, we can write little programs that in turn modify the 57 contents of Acme. 58 59 ## Plumbing Rules for Git 60 61 In Git, all objects (primarily commits, blobs, and trees) have a 62 SHA, which is a hexidecimal string, making it easy to match on. Git 63 likes to shorten them in tool output, so a primitive regex to match 64 them might be: 65 66 67 [a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+ 68 69 That's 6 or more hex digits in a row (an astute reader might warn 70 that this would also match, say, CSS color codes, but for our 71 purposes it'll work!). 72 73 The full plumbing rule looks like so (in `~/lib/plumbing`): 74 75 type is text 76 data matches '[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+' 77 plumb start rc -c 'git -C '$wdir' show '$0' >[2=1] | plumb -i -d edit -a ''action=showdata filename=/'$wdir'/'$0'''' 78 79 We tell the plumber that if the type of the message is text, and if 80 it matches our pattern, that it should start the external program `rc(1)` 81 (the plan9 shell) with the command: 82 83 git -C $wdir show $0 >[2=1] 84 85 To run git in the working directory of the buffer the click was 86 made on and run `git-show(1)` on the full text that was matched 87 (with stderr duped to stdout). 88 89 This command of course doesn't do much by itself--it will output 90 to stdout. So to make use of the output, we send it back to 91 `plumb(1)`, this time telling it to send to the `-d` (destination) 92 `edit` (editor) with the action of showing data. This will open a 93 new Acme buffer with the contents of the git object (whether it's 94 a commit, the file for the blob, etc). 95 96 97 ## Going Full GUI 98 99 At this point, we could run `git-log(1)` in a `win(1)` buffer (a 100 shell session in Acme) and start clicking SHAs to see the diffs, 101 but that's not super helpful. 102 103 Looking at how I like to interact with Git in Vim, I commonly do 104 the following: 105 106 107 1. Look at the full git log, with ability to dive into commits 108 2. View the short git log for a specific file to see when things changed at a high level 109 3. View the `git-blame(1)` to see when things changed at a line-level 110 111 The first can be solved by writing a small wrapper around git-log, `gl`: 112 113 #!/bin/sh 114 # git-log, but piped through plumb so that it: 115 # 1. doesn't scroll to the end 116 # 2. has the proper "filename" so that plumbing SHA's works 117 git log "$@" | plumb -i -d edit -a "action=showdata filename=/$(pwd)/git-log" 118 119 For a shortened version (easier to browse), we can simply call this 120 with `--oneline` (`glo`): 121 122 123 #!/bin/sh 124 exec gl --oneline "$@" 125 126 The output on this repo looks like so: 127 128 [/path/to/alexkarle.com/git-log Del Snarf Redo | Look] 129 28a5f1c blog: Edit typos and small rephrasings for wggen post 130 85b54e2 blog: Add post on wggen tool to manage wg creds 131 6c6d8e0 blog: Add post about garbash.com 132 da01dba blog: Add post about mandoc resume 133 ... 134 135 Finally, we can get a per-file history by just filtering `glo` (`gv`): 136 137 #!/bin/sh 138 # gv -- imitation of :GV! vim plugin 139 file=${1:-$samfile} 140 glo -- "$file" 141 142 Here we see a nice trick: `gv` takes in an argument (so that we can 143 specify the file to log), but if it's empty it defaults to `$samfile`, 144 which is set by Acme to be the current buffer being edited. So 145 putting `gv` in the tag of the README and middle clicking to execute 146 gives the same output as before, but filtered! 147 148 149 Git blame uses the same concepts (`gbl`): 150 151 #!/bin/sh 152 # git blame, in acme! 153 file=${1:-$samfile} 154 git blame --date="short" "$file" | plumb -i -d edit -a "action=showdata filename=$file:BLAME" 155 156 Calling `gbl` on the README pops up a new buffer with the following: 157 158 [/path/to/alexkarle.com/README.md:BLAME Del Snarf Redo | Look] 159 5e9783a6 (Alex Karle 2020-06-18 1) alexkarle.com 160 5e9783a6 (Alex Karle 2020-06-18 2) ============= 161 016a5d70 (Alex Karle 2020-07-14 3) My small corner of the internet. 162 163 This may seem unhelpful, but remember that clicking any of those SHA's will 164 open then in a new buffer with the full diff! Clicking 5e9783a6 gives: 165 166 [/path/to/alexkarle.com/5e9783a6 Del Snarf Redo | Look] 167 commit 5e9783a6ce559f76da83b2530059c9664f43306e 168 Author: Alex Karle <email @ domain> 169 Date: Thu Jun 18 00:18:48 2020 -0400 170 ... 171 ... 172 diff --git a/.gitignore b/.gitignore 173 new file mode 100644 174 index 0000000..84c048a 175 --- /dev/null 176 +++ b/.gitignore 177 @@ -0,0 +1 @@ 178 +/build/ 179 180 181 And what's this? More SHA's to click? 84c048a will give the full 182 contents of .gitignore at this commit (if the diff doesn't show 183 it). 184 185 The most incredible bit is that Acme doesn't need to do much to 186 support this use case--the flexibility of choosing an external (and 187 extensible) program to match the text and the ability to spawn new 188 buffers in Acme via any language externally enables this beautifully. 189 Compare this to the [~500L of VimL in 190 vim-fugitive](https://github.com/tpope/vim-fugitive/blob/9fcac5b/autoload/fugitive.vim#L6712) 191 to convert the Vim sidebar into an interactive blame viewer. (No 192 shade towards Tim Pope--if you're reading this, huge thanks for 193 everything you've ever written, it's inspired me and shaped my 194 career!). 195 196 197 198 ## Conclusion 199 200 I'm really glad I chose to revisit Acme. While it's probably never 201 going to replace Vim in my toolkit, it was such an incredible 202 experience to be able to turn the tool into a pretty solid Git GUI 203 (minus syntax highlighting of course) in just about an hour or so 204 (including time to learn plumbing rules). 205 206 The concept of a universal yet personally customizable plumber is 207 beyond cool, and the fact that Acme has one of the 3 buttons reserved 208 to plumb text allows for developers to easily hyperlink 209 plaintext, which is awesome. 210 211 If you'd like to give it a spin, all my scripts (including my starter 212 script for Acme) are Open Source and live in my 213 [dotfiles](https://sr.ht/~akarle/dotfiles). 214