Skip to content

Commit 4387762

Browse files
committed
Refactor lsp--create-filter-function for better performance
1 parent 09f16c7 commit 4387762

File tree

2 files changed

+47
-63
lines changed

2 files changed

+47
-63
lines changed

CHANGELOG.org

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
* Changelog
22
** Unreleased 9.0.1
3+
* refactor lsp--create-filter-function for better performance
34
* Add format on save support
45
* Fix beancount journal file init option
56
* Add support for [[https://github.com/glehmann/earthlyls][earthlyls]]

lsp-mode.el

Lines changed: 46 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7150,69 +7150,52 @@ server. WORKSPACE is the active workspace."
71507150
('request (lsp--on-request workspace json-data)))))))
71517151

71527152
(defun lsp--create-filter-function (workspace)
7153-
"Make filter for the workspace."
7154-
(let ((body-received 0)
7155-
leftovers body-length body chunk)
7156-
(lambda (_proc input)
7157-
(setf chunk (if (s-blank? leftovers)
7158-
(encode-coding-string input 'utf-8-unix t)
7159-
(concat leftovers (encode-coding-string input 'utf-8-unix t))))
7160-
7161-
(let (messages)
7162-
(while (not (s-blank? chunk))
7163-
(if (not body-length)
7164-
;; Read headers
7165-
(if-let* ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
7166-
;; We've got all the headers, handle them all at once:
7167-
(setf body-length (lsp--get-body-length
7168-
(mapcar #'lsp--parse-header
7169-
(split-string
7170-
(substring-no-properties chunk
7171-
(or (string-match-p "Content-Length" chunk)
7172-
(error "Unable to find Content-Length header."))
7173-
body-sep-pos)
7174-
"\r\n")))
7175-
body-received 0
7176-
leftovers nil
7177-
chunk (substring-no-properties chunk (+ body-sep-pos 4)))
7178-
7179-
;; Haven't found the end of the headers yet. Save everything
7180-
;; for when the next chunk arrives and await further input.
7181-
(setf leftovers chunk
7182-
chunk nil))
7183-
(let* ((chunk-length (string-bytes chunk))
7184-
(left-to-receive (- body-length body-received))
7185-
(this-body (if (< left-to-receive chunk-length)
7186-
(prog1 (substring-no-properties chunk 0 left-to-receive)
7187-
(setf chunk (substring-no-properties chunk left-to-receive)))
7188-
(prog1 chunk
7189-
(setf chunk nil))))
7190-
(body-bytes (string-bytes this-body)))
7191-
(push this-body body)
7192-
(setf body-received (+ body-received body-bytes))
7193-
(when (>= chunk-length left-to-receive)
7194-
(condition-case err
7195-
(with-temp-buffer
7196-
(apply #'insert
7197-
(nreverse
7198-
(prog1 body
7199-
(setf leftovers nil
7200-
body-length nil
7201-
body-received nil
7202-
body nil))))
7203-
(decode-coding-region (point-min)
7204-
(point-max)
7205-
'utf-8)
7206-
(goto-char (point-min))
7207-
(push (lsp-json-read-buffer) messages))
7208-
7209-
(error
7210-
(lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s"
7211-
(concat leftovers input)
7212-
err)))))))
7213-
(mapc (lambda (msg)
7214-
(lsp--parser-on-message msg workspace))
7215-
(nreverse messages))))))
7153+
"Efficient, low-latency filter for the workspace."
7154+
(let ((header-done nil)
7155+
(body-length nil)
7156+
(body-buffer (generate-new-buffer " *lsp-body*")))
7157+
(lambda (_proc input)
7158+
(with-current-buffer body-buffer
7159+
(goto-char (point-max))
7160+
;; Always insert input directly; avoid string concat.
7161+
(insert (encode-coding-string input 'utf-8-unix t))
7162+
(goto-char (point-min))
7163+
;; Loop for as long as there's a full message in the buffer.
7164+
(cl-loop
7165+
;; Stop only if at buffer end or incomplete message.
7166+
while (< (point) (point-max))
7167+
do
7168+
(unless header-done
7169+
(let ((header-end (search-forward "\r\n\r\n" nil t)))
7170+
(unless header-end
7171+
;; Incomplete header, wait for more input
7172+
(cl-return))
7173+
(let* ((headers (buffer-substring-no-properties (point-min) (- header-end 4)))
7174+
(parsed (mapcar #'lsp--parse-header (split-string headers "\r\n"))))
7175+
(setq body-length (lsp--get-body-length parsed))
7176+
(setq header-done t)
7177+
(delete-region (point-min) header-end)
7178+
(goto-char (point-min)))))
7179+
(when (and header-done body-length)
7180+
(let ((bytes-available (- (point-max) (point))))
7181+
(if (>= bytes-available body-length)
7182+
(let ((json-start (point))
7183+
(json-end (+ (point) body-length)))
7184+
(decode-coding-region json-start json-end 'utf-8)
7185+
(goto-char json-start)
7186+
(condition-case err
7187+
(let ((msg (lsp-json-read-buffer)))
7188+
;; IMMEDIATELY dispatch as soon as we get one full message:
7189+
(lsp--parser-on-message msg workspace))
7190+
(error
7191+
(lsp-warn "Failed to parse chunk with message %s" err)))
7192+
(delete-region (point-min) json-end)
7193+
;; Reset for next message
7194+
(setq header-done nil)
7195+
(setq body-length nil)
7196+
(goto-char (point-min))) ; Start parsing next message (if any)
7197+
;; Not enough body yet; wait for more data
7198+
(cl-return)))))))))
72167199

72177200
(defvar-local lsp--line-col-to-point-hash-table nil
72187201
"Hash table with keys (line . col) and values that are either point positions

0 commit comments

Comments
 (0)