Interacting with Acme

Posted on .

acme · plan9 · git

Interacting with Acme through the terminal is pretty rad. This is basically a poor rehash of acme(4) plus ideas for a Git integration.

Acme exposes all its innards through a file system structure. Running on a Linux system it’s supposedly possible to use FUSE to have the 9P file system directly available for reading and writing.

Without it, we can interact with Acme through the 9p command, which lets us read and write files on a 9P server. Acme’s files are all found in the acme folder. Each window that Acme has open is assigned a number, the window ID and these are present as folders named by their window IDs. Aside from the window folder, there are some special files and folders that we’ll talk about here.

The index file

The index file is a special file you can read to list all the windows currently open in Acme:

% 9p read acme/index
         15          68         799           0           1 /home/tj/sources/zx50ef/site/interacme.md Del Snarf Undo Put | Look
          2         105         112           1           0 /home/tj/sources/zx50ef/ Del Snarf Get | Look
         12          51         348           1           1 /home/tj/sources/zx50ef/site/ Del Snarf Get | Look
         13          62       11851           0           0 /home/tj/sources/zx50ef/+watch Del Snarf | Look Get Kill Quit
         14          49         205           0           1 /home/tj/sources/zx50ef/+Errors Del Snarf | Look

Each line represents a window. For every line, the first number in given is the window ID, so, as an example, we know there exists a folder called acme/15 (in fact the very window in which this is currently being typed!). The second and third numbers are character counts (in runes) in the tag and body, respectively. So acme/15 has 68 characters in the tag, and 799 characters in the body. The fourth number indicates if the window represents a file (0) or folder (1). The last number indicates if the window is modified (1 if true, 0 otherwise).

Window control

For each window, there are a number of ways to interact with them. We can read out the current tag and body, we can save the file, overwrite it, etc.

One could imagine an autosave feature that reads a given window corresponding to an open file, compares the last modified timestamp to the file and issues a Put if a significant amount of time has elapsed, like an autosave.

Creating a new window

We can open a new window like this:

% 9p read acme/new/ctl
         26          18           0           0           0         807 /mnt/font/DroidSans/11a/font          32

Simply by reading the special acme/new/ctl we get a new window created, and a line including the new window ID back.

Clearing a window

If $winid gives the window id, and we want to clear it:

% echo -n , | 9p write acme/$winid/addr
% echo -n | 9p write acme/$winid/data

The first line selects the entire buffer (the equivalent of executing “Edit ,”). The second line then writes the empty string as the data, overwriting the selection.

Reference: Agofmt, Watch

Ideas for Git integration

Getting used to editing scrollback, then selecting and executing the edited texts takes a bit of time, but it’s not too bad. It works for the most part, but I have been considering some of the ways I use Git and if a tighter integrcation could be done. Putting together a new patch, including writing the commit message could be done with a kind of “Git” buffer.

Some of the commands I use most frequently when putting a new commit:

  1. “git add -p” - interactively select (and edit) hunks that go on the index
  2. “git commit -v” - shows me the staged changes going into the commit

I rarely use the “-m” flag for git commit to provide a single-line message. Most of the time, I want to look over the change so it’s at hand when I’m writing out the why of the change. It’s also common to include a reference to an issue or task in a “Fixes: “ commit trailer.

In the tag of an open file/folder (say /path/to/x/), I write “Git” and execute it. A new buffer is opened with the tag:

/path/to/x/+git | Status Diff Log Commit

Executing “Status” would clear the buffer’s text and fill in the output from “git status”

Executing “Diff” would clear the buffer, and fill in the output from “git diff”. “Diff” should accepts extra arguments and just pass them to “git diff”.

Executing “Log” would clear the buffer, and fill in the output from “git log”. To manage the amount of output, maybe an implicit “-10” should be specified. “Log” could also accept extra arguments and just pass them to “git log”.

Executing “Commit” presents a bit of a challenge. If “-m” isn’t used for “git commit”, the user’s $EDITOR is opened with a template, and they can fill in the commit message. Once the file is closed the commit is created (provided the message isn’t empty). Maybe it would be possible to have Git see the opened file in Acme and only finalize the commit when the file is closed? If not, then another approach might be needed where the Git-Acme integration takes more control over it.

Today, when I want to write a longer commit message I do:

% mktemp
/tmp/tmp.UB8KlFSjLp

Then open the file (right-clicking on the name), type in the commit message and finalize the commit by executing:

git commit -F /tmp/tmp.UB8KlFSjLp

Then I also try to remember to delete the temporary file.