Function: eca-process--make-filter

eca-process--make-filter is a natively compiled function defined in eca-process.el.

Signature

(eca-process--make-filter HANDLE-MSG)

Documentation

Return a process filter function that parse JSON-RPC from stdout.

HANDLE-MSG is called for each complete message parsed.

The returned closure captures parsing state across invocations so that messages larger than a single OS pipe read (e.g. large write_file tool calls with full diffs) are reassembled correctly.

Source Code

;; Defined in /nix/store/jklqzw6pzz6j4jrbv8wy0iwgi7jlrbld-emacs-packages-deps/share/emacs/site-lisp/elpa/eca-20260928.1827/eca-process.el
(defun eca-process--make-filter (handle-msg)
  "Return a process filter function that parse JSON-RPC from stdout.
HANDLE-MSG is called for each complete message parsed.

The returned closure captures parsing state across invocations so that
messages larger than a single OS pipe read (e.g. large `write_file'
tool calls with full diffs) are reassembled correctly."
  (let (;; Persistent state across filter invocations:
        (leftovers nil)
        (body-length nil)
        (body-received 0)
        (body nil))
    (lambda (_proc raw-output)
      (let ((gc-cons-threshold (max gc-cons-threshold eca-min-gc-cons-threshold))
            chunk)
        (setf chunk (if (s-blank? leftovers)
                        (encode-coding-string raw-output 'utf-8-unix t)
                      (concat leftovers (encode-coding-string raw-output 'utf-8-unix t))))
        (setf leftovers nil)
        (let (messages)
          (while (not (s-blank? chunk))
            (if (not body-length)
                ;; Read headers
                (if-let* ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
                    ;; We've got all the headers, handle them all at once:
                    (setf body-length (let* ((headers (mapcar #'eca-process--parse-header
                                                              (split-string
                                                               (substring-no-properties chunk
                                                                                        (or (string-match-p "Content-Length" chunk)
                                                                                            (error "Unable to find Content-Length header"))
                                                                                        body-sep-pos)
                                                               "\r\n")))
                                             (content-length (cdr (assoc "Content-Length" headers))))
                                        (if content-length
                                            (string-to-number content-length)
                                          ;; This usually means either the server or our parser is
                                          ;; screwed up with a previous Content-Length
                                          (error "No Content-Length header")))
                          body-received 0
                          body nil
                          chunk (substring-no-properties chunk (+ body-sep-pos 4)))

                  ;; Haven't found the end of the headers yet. Save everything
                  ;; for when the next chunk arrives and await further input.
                  (setf leftovers chunk
                        chunk nil))
              (let* ((chunk-length (string-bytes chunk))
                     (left-to-receive (- body-length body-received))
                     (this-body (if (< left-to-receive chunk-length)
                                    (prog1 (substring-no-properties chunk 0 left-to-receive)
                                      (setf chunk (substring-no-properties chunk left-to-receive)))
                                  (prog1 chunk
                                    (setf chunk nil))))
                     (body-bytes (string-bytes this-body)))
                (push this-body body)
                (setf body-received (+ body-received body-bytes))
                (when (>= chunk-length left-to-receive)
                  (condition-case err
                      (with-temp-buffer
                        (apply #'insert
                               (nreverse
                                (prog1 body
                                  (setf body-length nil
                                        body-received 0
                                        body nil))))
                        (decode-coding-region (point-min)
                                              (point-max)
                                              'utf-8)
                        (goto-char (point-min))
                        (push (eca-api--json-read-buffer) messages))

                    (error
                     (setf body-length nil
                           body-received 0
                           body nil)
                     (eca-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s"
                               (concat leftovers raw-output)
                               err)))))))
          (mapc handle-msg
                (nreverse messages)))))))