Skip to content

Commit

Permalink
Update readme
Browse files Browse the repository at this point in the history
1. eglot
2. build instruction
3. better lsp-mode code (handle non-native-json version)
  • Loading branch information
blahgeek committed Jan 6, 2024
1 parent 7ef6849 commit 9369e6a
Showing 1 changed file with 44 additions and 16 deletions.
60 changes: 44 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Emacs lsp-mode performance booster
# Emacs LSP performance booster

Improve performance of [lsp-mode](https://github.com/emacs-lsp/lsp-mode) using a wrapper executable.
Improve performance of [lsp-mode](https://github.com/emacs-lsp/lsp-mode) or [eglot](https://github.com/joaotavora/eglot) using a wrapper executable.

## Background & Prior work

(Huge thanks to @yyoncho for both maintaining lsp-mode and giving me the inspiration of this project).

According to [yyoncho](https://www.reddit.com/r/emacs/comments/ymrkyn/comment/iv90q4i/?utm_source=share&utm_medium=web2x&context=3),
the are several performance issues related to lsp-mode:
the are several performance issues related to lsp-mode (mostly the same for elot):

1. Json parsing in Emacs is slow
2. The server may block on sending data to emacs when the buffer is full, because Emacs may consume the data too slo
Expand All @@ -17,6 +17,7 @@ the are several performance issues related to lsp-mode:
The result is very good regarding performance. However it requires modifications in Emacs source code and it seems unlikely that those changes would be merged into upstream.
Also, frankly I doubt that this would be well-maintained in the future since it also requires seperated code path in lsp-mode (I myself encountered some [issues](https://github.com/emacs-lsp/emacs/issues/12)).


## How this project work

This project provides an wrapper executable around lsp servers to work around above-mentioned issues:
Expand All @@ -34,32 +35,51 @@ Overall, this achieves similar result as the native async non-blocking jsonrpc a

## How to use

Prerequisite:
Generally, what you need to do is:

1. Wrap your lsp server command with this `emacs-lsp-booster` executable.
For example, if the original lsp server command is `pyright-langserver --stdio`, configure lsp-mode or eglot to run `emacs-lsp-booster pyright-langserver --stdio` instead.
2. Advise the json parsing function in lsp-mode or eglot to try to parse the input as bytecode instead of json.

See more detailed steps below.

### Obtain or build `emacs-lsp-booster`

For linux users, you may download the prebuilt binary from [release](https://github.com/blahgeek/emacs-lsp-booster/releases).
*(The macOS binary in the release page lacks proper code signing for now.)*

1. Emacs 28 or newer (NOT the [native-jsonrpc custom version](https://github.com/emacs-lsp/emacs)), recent version of lsp-mode
2. **Use [plist for deserialization](https://emacs-lsp.github.io/lsp-mode/page/performance/#use-plists-for-deserialization) for lsp-mode**
Or alternatively, you may build the target locally:

Steps:
1. Setup [Rust toolchain](https://rustup.rs/)
2. Run `cargo build --release`
3. Find the built binary in `target/release/emacs-lsp-booster`

1. Build the target or download it from [release](https://github.com/blahgeek/emacs-lsp-booster/releases), put the executable `emacs-lsp-booster` in $PATH (e.g. `~/.local/bin`)
2. Add the following code to your `init.el`:
Then, put the `emacs-lsp-booster` binary in your $PATH (e.g. `~/.local/bin`).

> NOTE: if your emacs does not have json support (aka, `(fboundp 'json-parse-buffer)` is nil),
> replace `json-parse-buffer` with `json-read` in the first line!
### Configure `lsp-mode`

> **For eglot users**: see https://github.com/blahgeek/emacs-lsp-booster/issues/1
(Make sure NOT to use the [native-jsonrpc custom version](https://github.com/emacs-lsp/emacs))

1. **Use [plist for deserialization](https://emacs-lsp.github.io/lsp-mode/page/performance/#use-plists-for-deserialization) for lsp-mode**
3. Add the following code to your `init.el`:

```elisp
(define-advice json-parse-buffer (:around (old-fn &rest args) lsp-booster-parse-bytecode)
(defun lsp-booster--advice-json-parse (old-fn &rest args)
"Try to parse bytecode instead of json."
(or
(when (equal (following-char) ?#)
(let ((bytecode (read (current-buffer))))
(when (byte-code-function-p bytecode)
(funcall bytecode))))
(apply old-fn args)))
(define-advice lsp-resolve-final-command (:around (old-fn cmd &optional test?) add-lsp-server-booster)
(advice-add (if (progn (require 'json)
(fboundp 'json-parse-buffer))
'json-parse-buffer
'json-read)
:around
#'lsp-booster--advice-json-parse)
(defun lsp-booster--advice-final-command (old-fn cmd &optional test?)
"Prepend emacs-lsp-booster command to lsp CMD."
(let ((orig-result (funcall old-fn cmd test?)))
(if (and (not test?) ;; for check lsp-server-present?
Expand All @@ -71,8 +91,16 @@ Steps:
(message "Using emacs-lsp-booster for %s!" orig-result)
(cons "emacs-lsp-booster" orig-result))
orig-result)))
(advice-add 'lsp-resolve-final-command :around #'lsp-booster--advice-final-command)
```

Done! Now try to use lsp-mode as usual.

You can verify that it works by checking that `emacs-lsp-booster` is running along with lsp servers and lsp-mode related functions works as expected.
### Configure `eglot`

Please see https://github.com/blahgeek/emacs-lsp-booster/issues/1 . Huge thanks to @jdtsmith

### How to verify it's working

1. Check that `emacs-lsp-booster` process is running
2. Check the stderr buffer (e.g. for lsp-mode, `*pyright::stderr*` buffer), it should contain `emacs_lsp_booster` related log.

0 comments on commit 9369e6a

Please sign in to comment.