| git.druid.rocks | index | druid520 | radium | bin/ | rd.sh |
bin/rd.sh
#!/usr/bin/env sh
# rd: a thin qol wrapper around emacsclient, kinda like doom emacs's
# own `doom' cli - not a build tool, not a package manager, just the
# handful of daemon interactions worth a shorter name than the real
# emacsclient invocation. install: symlink (or copy) this file
# somewhere on $PATH - it isn't under ~/.emacs.d itself (usage's own
# install step is `cp -r this dir to ~/.emacs.d', and that directory
# isn't normally on $PATH), so it needs its own, separate step:
# ln -s ~/.emacs.d/bin/rd.sh ~/bin/rd
# $RADIUM_CLIENT_ARGS: the flags a bare `rd' (or `rd FILE...') hands
# to emacsclient - -nw (a terminal frame) unless overridden. this is
# necessarily shell-side config, not ~/.radium.el: rd is its own,
# separate process, already running (and needing these flags) before
# it ever talks to the daemon that loads ~/.radium.el, so there's no
# elisp in the loop yet at the point this decision has to be made.
# export it from your shell rc instead, e.g. `export
# RADIUM_CLIENT_ARGS=-c` for a real graphical frame instead of -nw.
set -e
ipc="${RADIUM_IPC:-$HOME/.radium.ipc}"
client_args="${RADIUM_CLIENT_ARGS:--nw}"
usage() {
echo "usage: rd -ipc FORM | -eval EXPR | -start | -stop | -status" >&2
echo " rd [FILE...] (emacs wrapper: emacsclient $client_args FILE...)" >&2
echo " rd -- [FILE...] (same, forced - for a FILE that itself starts with '-')" >&2
exit 1
}
daemon_up() {
emacsclient --eval nil >/dev/null 2>&1
}
require_daemon() {
daemon_up || { echo "rd: emacs daemon is not running (rd -start)" >&2; exit 1; }
}
cmd="${1:-}"
case "$cmd" in
--)
# the one escape hatch out of the leading-dash convention below:
# a real filename that itself starts with '-' would otherwise be
# unreachable (it'd always be read as one of rd's own
# subcommands, or fail as an unrecognized one) - same '--' means
# "everything after this is positional" convention getopt/many
# unix tools already use.
shift
require_daemon
exec emacsclient $client_args "$@"
;;
-*)
# every one of rd's own subcommands is spelled with a leading
# '-', precisely so a bare `rd somefile' can never collide with
# one - no leading '-' at all always means "emacs wrapper",
# whatever the rest of argv is, below.
sub="${cmd#-}"
[ -n "$sub" ] || usage
shift
case "$sub" in
ipc)
# fire-and-forget: radium-ipc.el evals this and logs the
# result to the daemon's own *Messages*, nothing comes
# back down this pipe - see its own file for why that's
# the actual design, not a missing feature.
#
# "$*" (not "$@") on purpose below, in both ipc and eval:
# it joins every remaining word with a single space, which
# only matters (loses original spacing) if the form itself
# was passed as several unquoted shell words instead of one
# quoted string - the documented usage is always the latter
# (`rd -eval "(foo bar)"'), where "$*" and "$@" behave
# identically anyway.
[ "$#" -gt 0 ] || usage
require_daemon
[ -p "$ipc" ] || { echo "rd: $ipc is not a fifo (radium-ipc.el creates it on daemon startup)" >&2; exit 1; }
printf '%s\n' "$*" > "$ipc"
;;
eval)
[ "$#" -gt 0 ] || usage
require_daemon
emacsclient --eval "$*"
;;
status)
if daemon_up; then
echo "rd: daemon is up"
else
echo "rd: daemon is not running"
exit 1
fi
;;
start)
daemon_up && { echo "rd: daemon is already up" >&2; exit 1; }
exec emacs --daemon
;;
stop)
require_daemon
emacsclient --eval '(kill-emacs)' >/dev/null 2>&1 || true
;;
*)
usage
;;
esac
;;
*)
# emacs wrapper: no subcommand at all, just emacsclient with
# $RADIUM_CLIENT_ARGS plus whatever real args (files, typically)
# were actually given - unquoted on purpose, $client_args is
# meant to word-split into however many flags it holds.
require_daemon
exec emacsclient $client_args "$@"
;;
esac