Jacob Vosmaer's blog

Booting Acme on macOS

2020-09-13

Starting up Acme, the text editor I use, is non-trivial. It relies on two supporting processes (fontsrv and plumber) and it doesn't work well if you run more than one instance.

Besides this process management, it is also desirable to modify environment variables, because Acme is also my terminal emulator, and it is a "dumb" one meaning it needs special settings.

The idea of "run only one instance" sounds like a job for macOS launchd but I have come to not like that. I now just use a script that does an old fashioned double fork.

bin/acme

#!/usr/bin/env ruby

Dir.chdir(ENV['HOME'])

# Send SIGTERM to all running plan9port server processes
system(*%w[pkill 9pserve])

log = ['Library/Logs/acme.log', 'a']

# Prevent colorization settings leaking into Acme
%w[
  CLICOLOR
  GREP_OPTIONS
].each { |k| ENV.delete(k) }

{
  'PAGER' => '9 nobs',
  'BROWSER' => 'Google Chrome',
  'MAILER' => 'browser',
  'EDITOR' => '9 E',
  'GIT_EDITOR' => '9 E',
  'TERM' => 'dumb',
  'SHELL' => '/Users/jacobvosmaer/bin/rc-wrap',
}.each { |k, v| ENV[k] = v }

fork do
  Process.setsid
  $stdin.reopen('/dev/null')

  [
    %w[9 plumber],
    %w[9 fontsrv],
    %w[9 acme -l acme.dump],
  ].each { |cmd| spawn(*cmd, err: log, out: log) }
end

The pkill command at the top of the script takes care of the "only one Acme process" requirement in a very crude way, by terminating all plan9port server processes. It is a common problem to terminate acme itself but not its helpers, and sometimes that is bad. Now by running bin/acme I get a clean slate on all its processes.

bin/rc-wrap

You may have noticed the SHELL=rc-wrap bit. This is what rc-wrap does:

#!/bin/sh
exec rc -l "$@"

What is going on here is that I want all my shells inside plan9port to be rc -l, not rc. For whatever reason SHELL=rc -l is not allowed so I made this wrapper executable that adds the -l argument but avoids the space in SHELL.

What rc -l does is to enable loading its startup file $HOME/lib/profile. I like this because it lets me define rc functions as shortcuts for when I work in Acme's terminal.

rc functions

I use rc functions instead of plain scripts so that I can neatly "shadow" commands that work fine in regular terminal but not-fine in Acme. Mostly stuff with colorization, such as:

fn rspec { u bundle exec rspec --no-color $* }

u is a plan9port helper that removes plan9port from the PATH, making the non-plan9port ls, sed, kill, grep etc available.

The advantage of having this rspec shell function is that it is specific to plan9port and when I run rspec in a normal terminal I still get normal rspec.

Tags: acme

IndexContact