I'm using emacs + lsp-mode + rust-analyzer and I have the following workflow:
The problem is that in emacs, the variables to control the set of features and the target with which rust-analyzer is started are global variables, so if I change them to restart rust-analyzer on project A, then open project B, rust-analyzer would start with the set of features and the target of project A.
Another related problem is that if I change the features/target of project B, then want to change those of project A, the current variables value reflect project B, not A.
What I would like:
- Either a way to access the set of features and the target used by rust-analyzer within emacs. This way I could write a function to read those values from the server of the current buffer when I want to modify them.
- Or a way to have those variables "server"-local which would probably be a new concept. Those variables would be shared between buffers that share the same rust-analyzer server.
EDIT 1: Note that a possible workaround would be to use one emacs server per project. But that's not very convenient.
Anybody experienced similar issues? Or has a solution?
For a whole bunch of reasons, whose details I do not care to remember, but among which were things that sound like what you describe, I switched to using a separate Emacs process per project, quite some time ago. My quality of life improved as a consequence.
Ok, I managed to find a solution. I just hold a mapping from project root directory to project configuration and use lsp-mode hooks to set/unset the global configuration based on the mapping when needed. I adapted my old functions to edit the mapping instead of the global variables. Here's the whole setup:
;; Maps directory(string) to variable(symbol) to value
(setq ia0-lsp (make-hash-table :test 'equal))
(defun ia0-lsp-get (k)
(let ((h (gethash (lsp-workspace-root) ia0-lsp)))
(or (if h (gethash k h)) (eval k))))
(defun ia0-lsp-put (k v)
(let* ((w (lsp-workspace-root))
(h (gethash w ia0-lsp)))
(setq h (make-hash-table))
(puthash w h ia0-lsp))
(puthash k v h)))
(defun ia0-lsp-load (k)
(set k (ia0-lsp-get k)))
(defun edit-lsp-rust-features ()
"Edit lsp-rust-features and lsp-rust-no-default-features."
(let* ((old-features (ia0-lsp-get 'lsp-rust-features))
(old-no-default (ia0-lsp-get 'lsp-rust-no-default-features)))
(setq old-features (mapconcat 'identity old-features ","))
(when old-no-default (setq old-features (concat "=" old-features)))
(let ((new-features (read-string "Features? " old-features)))
(setq new-no-default (string-prefix-p "=" new-features))
(ia0-lsp-put 'lsp-rust-no-default-features new-no-default)
(if new-no-default (aset new-features 0 ?,))
(setq new-features (vconcat (split-string new-features "," t)))
(ia0-lsp-put 'lsp-rust-features new-features))))
(defun edit-lsp-rust-target ()
(let* ((old-target (ia0-lsp-get 'lsp-rust-analyzer-cargo-target))
(new-target (read-string "Target? " (or old-target ""))))
(setq new-target (if (string= new-target "") nil new-target))
(ia0-lsp-put 'lsp-rust-analyzer-cargo-target new-target)))
(defun ia0-lsp-workspace-folders-changed-functions (added removed)
(defun ia0-lsp-after-initialize-hook ()
(setq lsp-rust-no-default-features nil)
(setq lsp-rust-features )
(setq lsp-rust-analyzer-cargo-target nil))
I believe the official answer to "different projects have different values of variables" is dir locals which I indeed use to customize lsp-rust-analyzer-cargo-target for certain projects.
However, I haven't found a way to edit dir locals and update the values in all affected buffers, which is annoying because I have to edit the dir local, reopen one of the project's files, and then restart LSP every time I want to switch targets. I do this often enough that I wrote a function for it.
Exactly. I tried dir locals (it was suggested on the lsp-mode Discord thread where I ask the same question) but as you say it doesn't share the value between all buffers, it just reads it when a file is opened. So that's not a solution. This is why I implemented the hash map, because in that way the value is share among all buffers of the same project.