mu4e: use buffer for reading server output

mu4e used strings processing the server output (from mu4e server) and
interpreting the s-expressions, but it's cleaner and slightly faster to
use a buffer instead. So that is what we do now.
This commit is contained in:
Dirk-Jan C. Binnema
2026-07-27 19:39:02 +03:00
committed by Seth Ladygo
parent 702da53ad9
commit ca5e1d64c1

View File

@ -270,13 +270,21 @@ It is the mu4e-version of \"mu find <query> --analyze\"."
;;; Handling raw server data
(defvar mu4e--server-buf nil
"Buffer (string) for data received from the backend.")
(defconst mu4e--server-name " *mu4e-server*"
"Name of the server process, buffer.")
(defvar mu4e--server-process nil
"The mu-server process.")
(defun mu4e--server-buffer ()
"Get the buffer for accumulating data received from the backend.
Create it if needed. The buffer is unibyte, since the lengths in
the length cookies are in bytes."
(let ((buf (get-buffer-create mu4e--server-name)))
(with-current-buffer buf
(when enable-multibyte-characters
(set-buffer-multibyte nil)))
buf))
;; dealing with the length cookie that precedes expressions
(defconst mu4e--server-cookie-pre "\376"
"Each expression starts with a length cookie:
@ -301,33 +309,33 @@ Checks whether the server process is live."
(declare-function mu4e--massage-addresses "mu4e-contacts")
(defsubst mu4e--server-eat-sexp-from-buf ()
"Eat the next s-expression from `mu4e--server-buf'.
Note: this is a string, not an emacs-buffer. `mu4e--server-buf gets
its contents from the mu-servers in the following form:
(defun mu4e--server-eat-sexp-from-buf ()
"Eat the next s-expression from the server buffer.
The server buffer (see `mu4e--server-buffer') gets its contents
from the mu-server in the following form:
<`mu4e--server-cookie-pre'><length-in-hex><`mu4e--server-cookie-post'>
Function returns this sexp, or nil if there was none.
`mu4e--server-buf' is updated as well, with all processed sexp data
The server buffer is updated as well, with all processed sexp data
removed."
(ignore-errors ;; the server may die in the middle...
(let ((b (string-match mu4e--server-cookie-matcher-rx mu4e--server-buf))
(sexp-len) (objcons))
(when b
(setq sexp-len (string-to-number (match-string 1 mu4e--server-buf) 16))
;; does mu4e--server-buf contain the full sexp?
(when (>= (length mu4e--server-buf) (+ sexp-len (match-end 0)))
(with-current-buffer (mu4e--server-buffer)
(goto-char (point-min))
(when (re-search-forward mu4e--server-cookie-matcher-rx nil t)
(let ((sexp-len (string-to-number (match-string 1) 16)))
;; does the buffer contain the full sexp?
(when (>= (- (point-max) (point)) sexp-len)
;; clear-up start
(setq mu4e--server-buf (substring mu4e--server-buf (match-end 0)))
(delete-region (point-min) (point))
;; note: we read the input in binary mode -- here, we take the part
;; that is the sexp, and convert that to utf-8, before we interpret
;; it.
(setq objcons (read-from-string
(prog1
(car (read-from-string
(decode-coding-string
(substring mu4e--server-buf 0 sexp-len)
(buffer-substring-no-properties
(point-min) (+ (point-min) sexp-len))
'utf-8 t)))
(when objcons
(setq mu4e--server-buf (substring mu4e--server-buf sexp-len))
(car objcons)))))))
(delete-region (point-min) (+ (point-min) sexp-len)))))))))
(defun mu4e--server-plist-get (plist key)
"Like `plist-get' but load data from file if it is a string.
@ -415,7 +423,9 @@ The server output is as follows:
(:compose <reply|forward|edit|new> [:original<msg-sexp>] [:include <attach>])
`mu4e-compose-func'. :original looks like :view."
(mu4e-log 'misc "* Received %d byte(s)" (length str))
(setq mu4e--server-buf (concat mu4e--server-buf str)) ;; update our buffer
(with-current-buffer (mu4e--server-buffer) ;; update our buffer
(goto-char (point-max))
(insert str))
(let ((sexp (mu4e--server-eat-sexp-from-buf)))
(with-local-quit
(while sexp
@ -571,7 +581,8 @@ Not to be confused with the SCM/Guile REPL, as per
(mu4e--kill-stale)
(let* ((process-connection-type nil) ;; use a pipe
(args (mu4e--server-args)))
(setq mu4e--server-buf "")
(with-current-buffer (mu4e--server-buffer)
(erase-buffer))
(mu4e-log 'misc "* invoking '%s' with parameters %s" mu4e-mu-binary
(mapconcat (lambda (arg) (format "'%s'" arg)) args " "))
(setq mu4e--server-process (apply 'start-process
@ -599,9 +610,7 @@ Not to be confused with the SCM/Guile REPL, as per
;; try sending SIGINT (C-c) to process, so it can exit gracefully
(ignore-errors
(signal-process proc 'SIGINT))))
(setq
mu4e--server-process nil
mu4e--server-buf nil))
(setq mu4e--server-process nil))
;; error codes are defined in src/mu-util
;;(defconst mu4e-xapian-empty 19 "Error code: xapian is empty/non-existent")
@ -609,9 +618,12 @@ Not to be confused with the SCM/Guile REPL, as per
(defun mu4e--server-sentinel (proc _msg)
"Function called when the server process PROC terminates with MSG."
(let ((status (process-status proc)) (code (process-exit-status proc)))
(mu4e-log 'misc "* famous last words from server: '%s'" mu4e--server-buf)
(mu4e-log 'misc "* famous last words from server: '%s'"
(with-current-buffer (mu4e--server-buffer)
(buffer-string)))
(setq mu4e--server-process nil)
(setq mu4e--server-buf "") ;; clear any half-received sexps
(with-current-buffer (mu4e--server-buffer)
(erase-buffer)) ;; clear any half-received sexps
(cond
((eq status 'signal)
(cond