Less expensive jsonrpc logging (bug#79361)

Remove the oldest 1/4 of the jsonrpc events buffer when reaching the
size limit instead of just a few lines.  This reduces the cost of adding
a log entry from O(buffer-size) to O(1).

Also make messages forwarded to the events buffer, such as ones sent to
stderr from the server process, obey the same limit.

* lisp/jsonrpc.el (jsonrpc--limit-buffer-size): New.
(jsonrpc--log-event, jsonrpc--forwarding-buffer): Use it.
This commit is contained in:
Mattias Engdegård
2025-09-06 10:44:43 +02:00
parent 36c8ebe78a
commit c13c620f12

View File

@@ -995,6 +995,20 @@ TIMEOUT is nil)."
fn oops)
(remove-hook 'jsonrpc-event-hook fn)))))))
(defun jsonrpc--limit-buffer-size (max-size)
"Limit the current buffer to MAX-SIZE by eating lines at the beginning.
Do nothing if MAX-SIZE is nil."
(when max-size
(while (> (buffer-size) max-size)
(delete-region
(point-min)
(save-excursion
;; Remove 1/4, so that the cost is O(1) amortised, since each
;; call to `delete-region' will move the buffer contents twice.
(goto-char (+ (point-min) (/ (buffer-size) 4)))
(forward-line)
(point))))))
(defvar jsonrpc-event-hook (list #'jsonrpc--log-event)
"Hook run when JSON-RPC events are emitted.
This hooks runs in the events buffer of every `jsonrpc-connection'
@@ -1071,15 +1085,7 @@ of the API instead.")
(when error
(setq msg (propertize msg 'face 'error)))
(insert-before-markers msg)
;; Trim the buffer if it's too large
(when max
(save-excursion
(goto-char (point-min))
(while (> (buffer-size) max)
(delete-region (point) (progn (forward-line 1)
(forward-sexp 1)
(forward-line 2)
(point)))))))))))
(jsonrpc--limit-buffer-size max))))))
(defun jsonrpc--forwarding-buffer (name prefix conn)
"Helper for `jsonrpc-process-connection' helpers.
@@ -1093,19 +1099,23 @@ PREFIX to CONN's events buffer."
(add-hook
'after-change-functions
(lambda (beg _end _pre-change-len)
(cl-loop initially (goto-char beg)
do (forward-line)
when (bolp)
for line = (buffer-substring
(line-beginning-position 0)
(line-end-position 0))
do (with-current-buffer (jsonrpc-events-buffer conn)
(goto-char (point-max))
(let ((inhibit-read-only t))
(insert
(propertize (format "%s %s\n" prefix line)
'face 'shadow))))
until (eobp)))
(let* ((props (slot-value conn '-events-buffer-config))
(max (plist-get props :size)))
(unless (eql max 0)
(cl-loop initially (goto-char beg)
do (forward-line)
when (bolp)
for line = (buffer-substring
(line-beginning-position 0)
(line-end-position 0))
do (with-current-buffer (jsonrpc-events-buffer conn)
(goto-char (point-max))
(let ((inhibit-read-only t))
(insert
(propertize (format "%s %s\n" prefix line)
'face 'shadow))
(jsonrpc--limit-buffer-size max)))
until (eobp)))))
nil t))
(current-buffer)))