For a long time I used acme as my primary text editor. However, as time has passed, and I've used a real plan 9 system rather than plan9port more, I have come to prefer its older brother sam.
My biggest gripe with acme is that the window tags take up too much damn space!

The image above is tongue-in-cheek, but it becomes fairly hard to deal with in real use, too. When you plumb a file that's not open already, a new window is opened in acme. So even if you've arranged everything the way you want it, a new window usually means that any hidden windows are going to pop up again, and culling unneeded windows becomes something you have to think about.
The tag lines, which are touted as a convenient place to store file-specific commands an utilities, are too small to contain all the stuff you'd want to use, and too big to not distract you. A common piece of advice is to keep a "guide" file of expressions and commands that you can then chord into the window of your choice. After using sam, I find this inferior to the dedicated command buffer.
Acme tries too hard to be a window manager without being a window manager. You cannot, for example, run a graphical application in an acme window.
That said, I used acme for a long time, out of habit, and also because I thought it was more amenable to scripting, with its file-system interface. Then I was taught about sam scripts.
Sam scripts
9front added a few extremely useful commands to the sam command language:
^cmdwill runcmdand execute its standard output as sam commands_cmdis like^cmd, but feeds the current selection tocmd's standard input
Just these two additions enable the extension of sam with named commands of arbitrary complexity. While the command can be any executable, it's very easy and convenient to have a file of rc(1) functions that you can source from sam like so:
!. $home/lib/sam
Here's a few of mine that I hope demonstrate the concept.
Save (almost) all dirty files
Saving all dirty files is easy enough with the sam command language that it might not be worth having a special command for it.
However, I share it here because this variant skips files named +Error, which I will use later to hold the output of some commands.
fn T{
echo 'Y/^[^'']|\+Error/w'
}
Executing ^T in the command window will run the command.
Run a command
This runs a command and puts its stdout and stderr to a +Error window in the same directory as the last selected text window:
fn R{
f=$%
d=`{basename -d $f}
c=''''^($*)^''''
echo B $d/+Error # select +Error window
echo ',<' cd $d '&& >[2=1]' $c # overwrite contents w/cmd output
echo 0 # move to top of window
echo b $f # re-select original window
echo b ~~sam~~ # select cmd buffer
}
There is a bit of quoting that might intimidate newcomers to rc, but it just wraps each element in the list $* in quotes:
% echo ''''^(foo bar baz)^''''
'foo' 'bar' 'baz'
The output of the sam script has to get evaluated by a separate rc process, so the quotes are needed to prevent arguments with spaces from being split up.
Build a project
fn Build{
T
R /bin/mk $*
}
You can see here how sam scripts can be trivially composed together.
Using another 9front addition, the M command, we can add this to the mouse menu for a nice development loop:
Lookup a function definition
The 9front codebase follows the convention that the name of a function in a function definition starts on a new line:
long
strlen(char *s)
{
We can take advantage of that property in a sam script:
fn Cdef{
ident=`{read}
file=$%
cd `{basename -d $file}
addr=`:{g '^'^$"ident^'\(' | sed 1q}
if(! ~ $#addr 0)
plumb $addr(1)^:^$addr(2)
status=''
}
Since it must read the selected test, it should be called with the _ command:
It would be nicer if we did not have to select the surrounding text.
The _ command, like almost every sam command, can take an address.
The dimensions of the selected address are stored in the env variable $%dot as (line left right).
So we can do this:
fn Jdef{
if(~ $%dot(2) $%dot(3)){
echo '?[a-zA-Z0-9_]+?,/[a-zA-Z0-9_]+/_Cdef'
}
if not {
Cdef
}
}
If we combine this with the M command, using it looks like this:
You could imagine that the Cdef command could be arbitrarily complex[; it has access to the file name, so it could support multiple file types, and it could look for definitions in the standard library, and it could look for type definitions as well.
Take a look at the i command here.
In practice, a little bit goes a long way.
Gripes
I am enjoying using sam. It feels at the same time much simpler than acme yet more powerful and consistent. That is not to say it's without issues.
Drawing windows
Much like rio(1), when you create or resize a window in sam, you must sweep a rectangle to define its position and size.
So even though sam is quite good at working with lots of files thanks to its X command and dedicated command buffer, positioning those windows can be tedious.
I have an experimental patch that fills empty space when a window is created without sweeping its rectangle. It replaces the existing behavior, which just creates a really big window that doesn't overlap with the command window. It looks like this:
Going further: jamterm
qwx/fork contains jam and jamterm, a more ambitious modification of sam.
It has more intelligent window resizing, with snap-to-border behavior.
Its mouse menus have changed, with the initial click used to select the target window instead of requiring an additional click after selecting a menu item.
Instead of my approach above which creates +Error files in the working directory of whatever command is run to make plumbing work, jam introduces special handling of the file /tmp/jam.err file to accomplish the same.