branch: externals/async commit 3bd17d58e1ca14c629e72acfbd77138ff5adc1d6 Merge: 71cc50f27f a982ab7a9a Author: Thierry Volpiatto <thie...@posteo.net> Commit: GitHub <nore...@github.com>
Merge pull request #167 from Fuco1/feature/implement-child-messages feat: implement reading messages sent from child process --- async.el | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/async.el b/async.el index 244954feb4..3761354f73 100644 --- a/async.el +++ b/async.el @@ -197,6 +197,27 @@ It is intended to be used as follows: (process-name proc) (process-exit-status proc)))) (set (make-local-variable 'async-callback-value-set) t)))))) +(defun async-read-from-client (proc string) + ;; log the message in the process buffer + (with-current-buffer (process-buffer proc) + (insert string)) + + ;; parse message + (with-temp-buffer + (insert string) + (goto-char (point-min)) + (let (msg) + (condition-case nil + (while (setq msg (read (current-buffer))) + (when-let ((msg-decoded (ignore-errors (base64-decode-string msg)))) + (setq msg-decoded (car (read-from-string msg-decoded))) + (with-current-buffer (process-buffer proc) + (when async-callback + (funcall async-callback msg-decoded))))) + ;; This is OK, we reached the end of the chunk subprocess sent + ;; at this time. + (end-of-file t))))) + (defun async--receive-sexp (&optional stream) ;; FIXME: Why use `utf-8-auto' instead of `utf-8-unix'? This is ;; a communication channel over which we have complete control, @@ -280,20 +301,47 @@ its FINISH-FUNC is nil." #'identity async-callback-value (current-buffer)))))) (defun async-message-p (value) - "Return non-nil of VALUE is an async.el message packet." + "Return non-nil if VALUE is an async.el message packet." (and (listp value) (plist-get value :async-message))) -(defun async-send (&rest args) - "Send the given messages to the asynchronous Emacs PROCESS." +(defun async-send (process-or-key &rest args) + "Send the given message to the asychronous child or parent Emacs. + +To send messages from the parent to a child, PROCESS-OR-KEY is +the child process object. ARGS is a plist. Example: + + (async-send proc :operation :load-file :file \"this file\") + +To send messages from the child to the parent, PROCESS-OR-KEY is +the first key of the plist, ARGS is a value followed by +optionally more key-value pairs. Example: + + (async-send :status \"finished\" :file-size 123)" (let ((args (append args '(:async-message t)))) (if async-in-child-emacs - (if async-callback - (funcall async-callback args)) - (async--transmit-sexp (car args) (list 'quote (cdr args)))))) + (princ + (with-temp-buffer + (async--insert-sexp (cons process-or-key args)) + (buffer-string))) + (async--transmit-sexp process-or-key (list 'quote args))))) (defun async-receive () - "Send the given messages to the asynchronous Emacs PROCESS." + "Receive message from parent Emacs. + +The child process blocks until a message is received. + +Message is a plist with one key :async-message set to t always +automatically added to signify this plist is an async message. + +You can use `async-message-p' to test if the payload was a +message. + +Use + + (let ((msg (async-receive))) ...) + +to read and process a message." (async--receive-sexp)) ;;;###autoload @@ -310,6 +358,7 @@ working directory." (with-current-buffer buf (set (make-local-variable 'async-callback) finish-func) (set-process-sentinel proc #'async-when-done) + (set-process-filter proc #'async-read-from-client) (unless (string= name "emacs") (set (make-local-variable 'async-callback-for-process) t)) proc))) @@ -351,6 +400,16 @@ When done, the return value is passed to FINISH-FUNC. Example: (message \"Async process done, result should be 222: %s\" result))) +If you call `async-send' from a child process, the message will +be also passed to the FINISH-FUNC. You can test RESULT to see if +it is a message by using `async-message-p'. If nil, it means +this is the final result. Example of the FINISH-FUNC: + + (lambda (result) + (if (async-message-p result) + (message \"Received a message from child process: %s\" result) + (message \"Async process done, result: %s\" result))) + If FINISH-FUNC is nil or missing, a future is returned that can be inspected using `async-get', blocking until the value is ready. Example: