emacs-lsp / lsp-mode

Emacs client/library for the Language Server Protocol
https://emacs-lsp.github.io/lsp-mode
GNU General Public License v3.0
4.76k stars 874 forks source link

Add a variable for customizing `rust-analyzer.cargo.targetDir` #4506

Open fitzgen opened 1 month ago

fitzgen commented 1 month ago

The rust-analyzer.cargo.targetDir setting allows controlling the directory that rust-analyzer will use for build artifacts. It is useful to set this to something other than the default project's target directory because only one of cargo and rust-analyzer can use the directory at a time, they each exclusively lock it. This means that running tests and rust-analyzer running in the background can contend, which results in a poor user experience. Therefore, it would be nice if lsp-mode allowed configuring this setting via a custom variable or something.

More details: https://rust-analyzer.github.io/manual.html#rust-analyzer.cargo.targetDir

Thanks for all your work on lsp-mode!

apodolsk commented 3 weeks ago

Edit: well, no. The below does not work. I can see a didChangeConfiguration message getting logged, and its param JSON is identical to what I see with a known-working eglot-workspace-configuration, but rust-analyzer is not actually creating a subdirectory as it does with the eglot variant. I don't know enough about lsp-mode or the protocol to guess why, atm.

Based on https://github.com/emacs-lsp/lsp-mode/issues/167, it seems that lsp-mode does support passing/overriding LSP config options generically after initialization, via didChangeConfiguration LSP protocol messages. Here's my init.el snippet to set that up, including setting targetDir:

  (add-hook 'lsp-after-initialize-hook
            (lambda ()
              (lsp--set-configuration
               '(:rust-analyzer (:check (:ignore ["dead_code"
                                                  "unused_variables"
                                                  "unused_imports"])
                                        :cargo (:targetDir t
                                                    :features "all"))))))

I can see/debug the resulting didChangeConfiguration message via lsp-workspace-show-log.

As someone with 0 elisp knowledge, I found discussion of the similar eglot option to be useful for understanding the format of this thing: https://github.com/joaotavora/eglot/issues/363.

It's definitely nice to have prepacked per-LSP-server customize vars for common options, but I do also wish that we had this generic fallback as a first class documented and customizable setting.

apodolsk commented 2 weeks ago

I looked at this a bit more, though unfortunately the result is that I'm convinced that there's no way to set "override" options for rust-analyzer without changing lsp-mode sources.

The issue with lsp--set-configuration is that apparently the workspace/didChangeConfiguration protocol (where the LSP client notifies the server of config value changes) is at least "semi" deprecated in favor of workspace/configuration messages (where the server instead queries the client for config values). Rust analyzer still responds to didChangeConfiguration, but the response is apparently to ignore the config value payload and simply trigger a workspace/configuration message in reply to re-query the same values: https://github.com/rust-lang/rust-analyzer/blob/64c538f9fb3d490dd0c987df494d802977a5b9f0/crates/rust-analyzer/src/handlers/notification.rs#L201. I can again see in logs that both eglot and lsp-mode send the same didChangeConfiguration message, but only eglot actually responds to the resulting workspace/configuration request. lsp-mode leaves that message on read.

According to https://github.com/emacs-lsp/lsp-mode/issues/3686, there's an alternate mechanism to generically set options, via lsp-register-custom-settings. However, I couldn't get it to work, and I think that there's just a bug in the lsp-mode rust-analyzer integration which makes this method impossible. Namely, the issue is that, while the rls integration queries the "custom settings" via (lsp-configuration-section "rust") when setting up the language server (https://github.com/emacs-lsp/lsp-mode/blob/c36b95be6625dac5a37d3874a1a738e0c84ac39f/clients/lsp-rust.el#L341), the rust-analyzer integration skips that and just produces a config object in a more hardcoded way - it looks at pre-registered custom variables, but doesn't seem to integrate at all with the lsp-register-custom-settings system (https://github.com/emacs-lsp/lsp-mode/blob/c36b95be6625dac5a37d3874a1a738e0c84ac39f/clients/lsp-rust.el#L1655).

Since it seems like post-startup configuration via workspace/configuration is basically unimplemented, and startup-time configuration respects only a hardcoded set of options, I don't think there's a way to provide "extra" options to rust analyzer. Though the latter seems like it could be a straightforward bugfix (ideally for someone who actually knows elisp...).