Create a gist now

Instantly share code, notes, and snippets.

#!/usr/bin/env zsh
# ----------------------------------------------------------------------
# Send the standard input into a running emacs server, either on the
# kill ring, in a specified register, or in a new buffer. Examples:
# echo -n some text into the kill ring | into-emacs
# echo -n other text into register 1 | into-emacs -r 1
# long_command | into-emacs -b "long command output"
# Caveats:
# - If -b (buffer name) is used, -r (register name) is ignored.
# - The name given to -m (mode) must match an emacs mode function
# - (i.e. -m foo will execute (foo-mode) in emacs).
# - Only the first character of the argument to the -r flag is
# significant (as emacs registers are named by a single character).
# Any more characters will be ignored.
# ----------------------------------------------------------------------
# Buffer name to place the text into.
buffer_name=
# Mode to switch to for the buffer, if any
buffer_mode=
# Register to place the text into. If empty (the default), the text
# will be added to the kill ring.
register=
# Command-line flags:
# -r <register> : Put the text into a register instead of the kill ring
# -b <name> : Put the text into a buffer with the given name
# -m <mode> : When used with -b, switch the buffer to the given mode
while getopts r:b:m: opt; do
case "$opt" in
r) register=$OPTARG;;
b) buffer_name=$OPTARG;;
m) buffer_mode=$OPTARG;;
\?) # unknown flag
echo >&2 \
"usage: $0 [-r register | -b name [-m mode]]"
exit 1;;
esac
done
# Create a temporary directory holding a named pipe which will be
# removed when this script exits
umask 077
tmpdir=`mktemp -d /tmp/into-emacs-XXXXXX`
trap 'rm -rf "${tmpdir}"' EXIT INT TERM HUP
pipe="${tmpdir}/pipe"
mkfifo "${pipe}"
# Send the standard input of the script into the pipe (and on to
# emacs) in the background. (This requires some file descriptor
# gymnastics, as background processes often get /dev/null as their
# standard input rather than inheriting the standard input of the
# caller.)
exec 3<&0
cat <&3 3<&- > $pipe &
exec 3<&-
# Build up elisp to have emacs evaluate
if [ -n "${buffer_name}" ]; then
# Create a buffer with the specified name, possibly uniquified.
# Read from the pipe, mark the buffer as unmodified and read only,
# and switch to it. If requested, set the mode of the buffer.
# Add a local keybinding for "C-x #" to kill the buffer and close
# the frame. (This isn't a normal edit server buffer, but this
# preserves the behaior as if it were.)
from_pipe="(insert-file-contents \"${pipe}\") (not-modified) (read-only-mode)"
show_buf="(switch-to-buffer (current-buffer))"
set_mode=""
if [ -n "${buffer_mode}" ]; then
set_mode="(${buffer_mode}-mode)"
fi
exit_key="(local-set-key (kbd \"C-x #\") (lambda () (interactive) (kill-buffer (current-buffer)) (delete-frame (selected-frame))))"
to_eval="(with-current-buffer (generate-new-buffer \"*${buffer_name}*\") ${from_pipe} ${set_mode} ${show_buf} ${exit_key})"
# Run emacsclient in the foreground (which it needs to be, since
# we will interact with it)
emacsclient -t --eval "${to_eval}"
else
# Read from our named pipe into a string value
read_pipe="(with-temp-buffer (insert-file-contents \"${pipe}\") (buffer-string))"
if [ -n "${register}" ]; then
# Put the text into the specified register
to_eval="(set-register (string-to-char \"${register}\") ${read_pipe})"
else
# No register specified: put the text on the kill ring
to_eval="(kill-new ${read_pipe})"
fi
# Have emacs read from the pipe but discard its output
emacsclient --eval "${to_eval}" < /dev/null > /dev/null
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment