Skip to content

Instantly share code, notes, and snippets.

@nicferrier
Created October 29, 2011 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicferrier/1324488 to your computer and use it in GitHub Desktop.
Save nicferrier/1324488 to your computer and use it in GitHub Desktop.
(defmacro elnode-worker-elisp (output-stream &rest body)
"Evaluate the BODY in a child Emacs connected to OUTPUT-STREAM.
The child Emacs has a 'load-path' exactly as the 'load-path' of
the parent Emacs at execution.
The created child Emacs process is returned. It's possible to
kill the child Emacs process or do other things to it directly.
This could be very dangerous however, you should know what you
are doing before attempting it.
The OUTPUT-STREAM could be a buffer, a function or another
process.
If the OUTPUT-STREAM is another process it may have a process
property ':send-string-function' evaluating to a function to send
data to that process. The function should take the same
arguments as the standard Emacs Lisp 'process-send-string'.
Furthermore, if the OUTPUT-STREAM is another process, when the
child Emacs finishes an EOF is sent to that process. If the
OUTPUT-STREAM process has a process property ':send-eof-function'
then that is used to send the EOF. The function should take the
same arguments as the standard Emacs Lisp 'process-send-eof'.
An example:
(elnode-worker-elisp http-connection
(require 'creole)
(creole-wiki some-file-name))
Presuming http-connection is a process (in the manner of Elnode,
for example) this will cause a child Emacs to be created, within
which 'some-file-name' will be loaded and converted from
WikiCreole to HTML and then sent to the standard output
stream. The child's standard output stream is connected directly
to the 'http-connection'. In this case, presumably the
'http-connection' would have functions attached to the properties
'':send-string-function' and ':send-eof-function' to do HTTP
chunk encoding and to end the HTTP connection correctly."
(declare (indent defun))
(let ((loadpathvar (make-symbol "load-path-form"))
(childlispvar (make-symbol "child-lisp"))
(lispvar (make-symbol "lisp"))
(filtervar (make-symbol "filter-function"))
(cmdvar (make-symbol "command"))
(procvar (make-symbol "process"))
(namevar (make-symbol "process-name"))
(bufvar (make-symbol "buffer"))
(outvar (make-symbol "output-stream")))
`(let* ((,outvar ,output-stream)
(,childlispvar ; the lisp to run
(format "(progn (setq load-path (quote %S)) %S)\n"
load-path
'(progn ,@body)))
(,cmdvar "emacs -q -batch --eval '(eval (read))' 2> /dev/null")
(,namevar (concat
(number-to-string (random))
(number-to-string (float-time))))
;; We have to make a buffer unless the output-stream is a buffer
(,bufvar (cond
((bufferp ,outvar) ,outvar)
(t
(get-buffer-create (format "* %s *" ,namevar)))))
(,procvar (start-process-shell-command ,namevar ,bufvar ,cmdvar)))
;; If the output stream is not a buffer we need a filter function
(cond
;; We wrap output to filters functions or buffers just a little
((or (bufferp ,outvar)
(functionp ,outvar))
(set-process-filter
,procvar
(lambda (process data)
(if (equal data "Lisp expression: ")
(process-send-string process ,childlispvar)
(if (bufferp ,outvar)
(with-current-buffer ,outvar
(insert data))
(funcall ,outvar process data))))))
;; A process - setup a filter function
((processp ,outvar)
(set-process-filter
,procvar
(lambda (process data)
;; We get this as a signal to read a lisp expression
(if (equal data "Lisp expression: ")
(process-send-string process ,childlispvar)
(if (not (equal "closed" (process-status ,procvar)))
(when (processp ,outvar)
(funcall
;; Does the output-stream have a specific function?
(or (process-get ,outvar :send-string-function)
'process-send-string)
;; The data to sent to the output-stream process
,outvar data))))))))
;; Now setup the sentinel
(set-process-sentinel
,procvar
(lambda (process status)
(let ((send-eof-function
;; Does the output-stream have a send-eof?
(and (processp ,outvar)
(or (process-get ,outvar :send-eof-function)
'process-send-eof))))
(cond
((equal status "finished\n")
(message "%s completed" ,namevar)
(when send-eof-function
(funcall send-eof-function ,outvar)))
((string-match "exited abnormally with code \\([0-9]+\\)\n" status)
(message "%s completed with an error" ,namevar)
(when send-eof-function
(funcall send-eof-function ,outvar))
(delete-process process)
(unless (bufferp ,outvar)
(kill-buffer (process-buffer process))))
;; Any other signal status is ignored
(t)))))
,procvar)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment