Add lsp-booster

This commit is contained in:
Tristan D. 2024-08-26 12:05:57 +02:00
parent bcd279b56d
commit 0bfa64b4ba
Signed by: tristan
SSH key fingerprint: SHA256:9oFM1J63hYWJjCnLG6C0fxBS15rwNcWwdQNMOHYKJ/4
7 changed files with 204 additions and 8 deletions

View file

@ -0,0 +1,124 @@
;;; eglot-booster.el --- Boost eglot using lsp-booster -*- lexical-binding: t; -*-
;; Copyright (C) 2024 J.D. Smith
;; Author: J.D. Smith
;; Homepage: https://github.com/jdtsmith/eglot-booster
;; Package-Requires: ((emacs "29.1") (jsonrpc "1.0") (eglot "1.0") (seq "2.24"))
;; Version: 0.0.1
;; Keywords: convenience, programming
;; Prefix: eglot-booster
;; Separator: -
;; eglot-booster is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;; eglot-booster is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This small minor mode boosts eglot with emacs-lsp-booster.
;; Using it is simple:
;;
;; 1. Download/build a recent emacs-lsp-booster from
;; https://github.com/blahgeek/emacs-lsp-booster using the
;; instructions there.
;; 2. Enable eglot-booster-mode either in your init or, interactively,
;; use M-x eglot-booster-mode.
;; 3. Use eglot like normal.
;;
;; You can disable boosting by turning the minor mode off; any boosted
;; eglot servers will need to be restarted. Note: boosting works only
;; with local lsp servers programs which communicate via standard
;; input/output, not remote or network-port LSP servers.
;;; Code:
(require 'eglot)
(require 'jsonrpc)
(defcustom eglot-booster-no-remote-boost nil
"If non-nil, do not boost remote hosts."
:group 'eglot
:type 'boolean)
(defun eglot-booster-plain-command (com)
"Test if command COM is a plain eglot server command."
(and (consp com)
(not (integerp (cadr com)))
(not (memq :autoport com))))
(defvar-local eglot-booster-boosted nil)
(defun eglot-booster--jsonrpc--json-read (orig-func)
"Read JSON or bytecode, wrapping the ORIG-FUNC JSON reader."
(if eglot-booster-boosted ; local to process-buffer
(or (and (= (following-char) ?#)
(let ((bytecode (read (current-buffer))))
(when (byte-code-function-p bytecode)
(funcall bytecode))))
(funcall orig-func))
;; Not in a boosted process, fallback
(funcall orig-func)))
(defun eglot-booster--init (server)
"Register eglot SERVER as boosted if it is."
(when-let ((server)
(proc (jsonrpc--process server))
(com (process-command proc))
(buf (process-buffer proc)))
(unless (and (file-remote-p default-directory) eglot-booster-no-remote-boost)
(if (file-remote-p default-directory) ; com will likely be /bin/sh -i or so
(when (seq-find (apply-partially #'string-search "emacs-lsp-booster")
(process-get proc 'remote-command)) ; tramp applies this
(with-current-buffer buf (setq eglot-booster-boosted t)))
(when (string-search "emacs-lsp-booster" (car-safe com))
(with-current-buffer buf (setq eglot-booster-boosted t)))))))
(defvar eglot-booster--boost
'("emacs-lsp-booster" "--json-false-value" ":json-false" "--"))
(defun eglot-booster--wrap-contact (args)
"Wrap contact within ARGS if possible."
(let ((contact (nth 3 args)))
(cond
((and eglot-booster-no-remote-boost (file-remote-p default-directory)))
((functionp contact)
(setf (nth 3 args)
(lambda (&optional interactive)
(let ((res (funcall contact interactive)))
(if (eglot-booster-plain-command res)
(append eglot-booster--boost res)
res)))))
((eglot-booster-plain-command contact)
(setf (nth 3 args) (append eglot-booster--boost contact))))
args))
;;;###autoload
(define-minor-mode eglot-booster-mode
"Minor mode which boosts plain eglot server programs with emacs-lsp-booster.
The emacs-lsp-booster program must be compiled and available on
variable `exec-path'. Only local stdin/out-based lsp servers can
be boosted."
:global t
:group 'eglot
(cond
(eglot-booster-mode
(unless (executable-find "emacs-lsp-booster")
(setq eglot-booster-mode nil)
(user-error "The emacs-lsp-booster program is not installed"))
(advice-add 'jsonrpc--json-read :around #'eglot-booster--jsonrpc--json-read)
(advice-add 'eglot--connect :filter-args #'eglot-booster--wrap-contact)
(add-hook 'eglot-server-initialized-hook #'eglot-booster--init))
(t
(advice-remove 'jsonrpc--json-read #'eglot-booster--jsonrpc--json-read)
(advice-remove 'eglot--connect #'eglot-booster--wrap-contact)
(remove-hook 'eglot-server-initialized-hook #'eglot-booster--init))))
(provide 'eglot-booster)
;;; eglot-booster.el ends here

View file

@ -0,0 +1,33 @@
;;; lsp-booster.el --- Boost emacs-lsp using lsp-booster -*- lexical-binding: t; -*-
(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)))
(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?
(not (file-remote-p default-directory)) ;; see lsp-resolve-final-command, it would add extra shell wrapper
lsp-use-plists
(not (functionp 'json-rpc-connection)) ;; native json-rpc
(executable-find "emacs-lsp-booster"))
(progn
(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)
(provide 'lsp-booster)
;;; lsp-booster.el ends here

View file

@ -131,6 +131,17 @@
:config
(global-activity-watch-mode))
;; lsp-booster
(use-package lsp-booster
:after lsp)
(use-package eglot-booster
:after eglot
:config
(eglot-booster-mode))
;; rust
(setq! lsp-inlay-hint-enable t)
(setq! lsp-rust-analyzer-cargo-run-build-scripts t)

29
flake.lock generated
View file

@ -201,6 +201,26 @@
"type": "github"
}
},
"emacs-lsp-booster": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1716274896,
"narHash": "sha256-WsyEkdt8ReGQ40+yV4Cb99A2MEmV0O/i6rmFQura5ww=",
"owner": "slotThe",
"repo": "emacs-lsp-booster-flake",
"rev": "7d110295988fc9bf7fd43bb0cabfbe58a4a5ecf8",
"type": "github"
},
"original": {
"owner": "slotThe",
"repo": "emacs-lsp-booster-flake",
"type": "github"
}
},
"emacs-overlay": {
"inputs": {
"flake-utils": [
@ -214,17 +234,17 @@
]
},
"locked": {
"lastModified": 1723799695,
"narHash": "sha256-8W/xxCpwQC9LOnwUO0Q7aGAF463ozaxcwQ6/toqtz0M=",
"lastModified": 1724662747,
"narHash": "sha256-8ehh6fm5C7TRNc3HmTr9KCyi7FqfFR1MqlqdynLKrMA=",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "3b4f8179de2b4950540d70161854e43fe1010eae",
"rev": "3052bb01d404ee9bd03b040c9ae898febca05b81",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "3b4f8179de2b4950540d70161854e43fe1010eae",
"rev": "3052bb01d404ee9bd03b040c9ae898febca05b81",
"type": "github"
}
},
@ -806,6 +826,7 @@
"inputs": {
"chaotic": "chaotic",
"disko": "disko",
"emacs-lsp-booster": "emacs-lsp-booster",
"emacs-overlay": "emacs-overlay",
"envfs": "envfs",
"flake-compat": "flake-compat",

View file

@ -61,11 +61,15 @@
inputs.nixpkgs.follows = "nixpkgs";
};
emacs-overlay = {
url = "github:nix-community/emacs-overlay/3b4f8179de2b4950540d70161854e43fe1010eae";
url = "github:nix-community/emacs-overlay/3052bb01d404ee9bd03b040c9ae898febca05b81";
inputs.flake-utils.follows = "flake-utils";
inputs.nixpkgs-stable.follows = "nixpkgs-stable";
inputs.nixpkgs.follows = "nixpkgs";
};
emacs-lsp-booster = {
url = "github:slotThe/emacs-lsp-booster-flake";
inputs.nixpkgs.follows = "nixpkgs";
};
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
@ -128,6 +132,7 @@
, home-manager
, plasma-manager
, emacs-overlay
, emacs-lsp-booster
, nur
, nix-index-database
, disko
@ -147,6 +152,7 @@
overlays = [
my-overlay
emacs-overlay.overlay
emacs-lsp-booster.overlays.default
inputs.nix-alien.overlays.default
inputs.nix-ld-rs.overlays.default
];

View file

@ -9,7 +9,7 @@
};
programs.thunderbird = {
enable = true;
package = pkgs.betterbird;
package = pkgs.thunderbird-128;
profiles."main" = {
isDefault = true;
};

View file

@ -9,14 +9,14 @@ let
doomemacsSrc = builtins.fetchGit {
url = "https://github.com/doomemacs/doomemacs";
ref = "master";
rev = "1912571c9cec35d06f869637573e75ec529f6c7c";
rev = "c862968f4843fa7c30b9dbca688d8e400729196b";
};
neofetchThemesSrc = builtins.fetchGit {
url = "https://github.com/Chick2D/neofetch-themes";
ref = "main";
rev = "c7392136bed264258c9b8788b14410e1ff06d602";
};
myEmacs = (pkgs.emacsPackagesFor pkgs.emacs-pgtk).emacsWithPackages (epkgs:
myEmacs = (pkgs.emacsPackagesFor pkgs.emacs-unstable-pgtk).emacsWithPackages (epkgs:
with epkgs; [
vterm
treesit-grammars.with-all-grammars
@ -317,6 +317,7 @@ in
# TODO Disable gui apps & switch to emacs-git instead of pgtk on headless systems
packages = with pkgs; [
myEmacs
emacs-lsp-booster
any-nix-shell
atool
aspell