Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for setting cargo features via rust-analyzer.cargo.features #3375

Closed
ia0 opened this issue Mar 2, 2022 · 16 comments
Closed

Support for setting cargo features via rust-analyzer.cargo.features #3375

ia0 opened this issue Mar 2, 2022 · 16 comments

Comments

@ia0
Copy link
Contributor

ia0 commented Mar 2, 2022

Hi,

I would like to change the list of features that rust-analyzer is using to compile the code (from within emacs, so without changing the default features in the Cargo.toml). Searching on internet, it looks like there is some rust-analyzer.cargo.features configuration for that. But I can't see it in lsp-mode. The closest I see is lsp-rust-analyzer-cargo-override-command but I don't know how to use it.

I didn't find any mention of this feature request in the wishlist.

Thanks!

@brotzeit
Copy link
Member

brotzeit commented Mar 2, 2022

It's probably a little hard to find it in this case.

    :cargo (:allFeatures ,(lsp-json-bool lsp-rust-all-features)
            :noDefaultFeatures ,(lsp-json-bool lsp-rust-no-default-features)
            :features ,lsp-rust-features

I think you are looking for lsp-rust-features.

@ia0
Copy link
Contributor Author

ia0 commented Mar 2, 2022

Thanks @brotzeit !

Yes looks like it works. Here is what I do:

  • set-variable then lsp-rust-features and ["my_feature"]
  • lsp-workspace-restart and select the only option

I'll have to see how to simplify this process, but that's already great.

@brotzeit
Copy link
Member

brotzeit commented Mar 2, 2022

You can use setq instead of set-variable and put it in your emacs config.

@ia0
Copy link
Contributor Author

ia0 commented Mar 2, 2022

I mean more something like a function that permits to edit the variable to easily add or remove features and that would automatically restart rust-analyzer (or if it's possible to push the config, it's even better). But for that I'll need to learn elisp again, so it will wait this week-end.

@brotzeit
Copy link
Member

brotzeit commented Mar 2, 2022

Have fun ;)

Is your question answered ?

@ia0
Copy link
Contributor Author

ia0 commented Mar 2, 2022

Yes thanks! :-)

@ia0 ia0 closed this as completed Mar 2, 2022
@ia0
Copy link
Contributor Author

ia0 commented Mar 2, 2022

For posterity, here is what I did to edit the features:

(defun edit-lsp-rust-features ()
  "Edit lsp-rust-features."
  (interactive)
  (setq lsp-rust-features
        (read-minibuffer "Features? " (format "%S" lsp-rust-features))))

I wasn't able to call lsp-workspace-restart automatically because it takes an argument and I don't know how to get it. But I've just bound both commands to a shortcut so I can just do:

  • C-c f edit features
  • C-c w r

Thanks again for pointing me out to lsp-rust-features!

@yyoncho
Copy link
Member

yyoncho commented Mar 3, 2022

@ia0 FYI there is M-x customize-variable

@ia0
Copy link
Contributor Author

ia0 commented Mar 3, 2022

Yes I've seen this too, but it's very unconvenient to use. The cursor is not directly on the old value and Enter doesn't change the value. You need to click Apply. That's too many extra-steps compared to directly editing the value.

@yorodm
Copy link

yorodm commented Mar 8, 2022

Another take on @ia0 function, I find more convenient to just type feature-a,feature-b

(defun edit-lsp-rust-features ()
    "Edit lsp-rust-features."
    (interactive)
    (setq lsp-rust-features
          (let ((current-features (mapconcat 'identity lsp-rust-features ",")))
            (vconcat (s-split "," (read-string "Features? " current-features))))))

@ia0
Copy link
Contributor Author

ia0 commented Mar 8, 2022

Thanks @yorodm ! I was actually having issues when typing multiple features today that I had to update the prompt to remind me that the syntax is ["foo" "bar"] and not ["foo", "bar"]. I'll try your function next time I get the chance.

@ia0
Copy link
Contributor Author

ia0 commented Mar 9, 2022

I modified it a bit to correctly map the empty string to the empty vector:

(defun edit-lsp-rust-features ()
  "Edit lsp-rust-features."
  (interactive)
  (let ((old-features (mapconcat 'identity lsp-rust-features ",")))
  (let ((new-features (read-string "Features? " old-features)))
  (setq lsp-rust-features (vconcat (split-string new-features "," t))))))

@ia0
Copy link
Contributor Author

ia0 commented Mar 17, 2022

For posterity, I improved the command to support lsp-rust-no-default-features:

(defun edit-lsp-rust-features ()
  "Edit lsp-rust-features and lsp-rust-no-default-features."
  (interactive)
  (let ((old-features (mapconcat 'identity lsp-rust-features ",")))
  (when lsp-rust-no-default-features
    (setq old-features (concat "=" old-features)))
  (let ((new-features (read-string "Features? " old-features)))
  (setq lsp-rust-no-default-features (string-prefix-p "=" new-features))
  (if lsp-rust-no-default-features (aset new-features 0 ?,))
  (setq lsp-rust-features (vconcat (split-string new-features "," t))))))

The syntax is an optional = (i.e. set instead of add features) followed by a comma-separated list of features:

  • foo,bar is like --features=foo,bar
  • =foo,bar is like --no-default-features --features=foo,bar

@andrewbanchich
Copy link

I'm trying to get this working at the moment but I just get Symbol's value as variable is void: lsp-rust-features.

@yyoncho Could lsp-mode provide a high level setting for this similar to VS Code?

@ntc2
Copy link
Contributor

ntc2 commented Sep 2, 2024

I'm finding that adding a feature to lsp-rust-features isn't enough to make rust-analyzer actually cargo check the code that is hidden behind that feature: I also need to add the feature to lsp-rust-analyzer-checkonsave-features! Adding the feature in both places seems to be equivalent to adding it to the default features in Cargo.toml, but adding the feature in only one of lsp-rust-features or lsp-rust-analyzer-checkonsave-features is not enough.

Looking at the part of the code that @brotzeit linked to earlier, I see that it has "features" configs in two places:

  1. the :cargo : features config uses lsp-rust-features:

    lsp-mode/clients/lsp-rust.el

    Lines 1683 to 1685 in c36b95b

    :cargo ( :allFeatures ,(lsp-json-bool lsp-rust-all-features)
    :noDefaultFeatures ,(lsp-json-bool lsp-rust-no-default-features)
    :features ,lsp-rust-features
  2. the :checkOnSave :features config uses lsp-rust-analyzer-checkonsave-features:

    lsp-mode/clients/lsp-rust.el

    Lines 1669 to 1673 in c36b95b

    :checkOnSave ( :enable ,(lsp-json-bool lsp-rust-analyzer-cargo-watch-enable)
    :command ,lsp-rust-analyzer-cargo-watch-command
    :extraArgs ,lsp-rust-analyzer-cargo-watch-args
    :allTargets ,(lsp-json-bool lsp-rust-analyzer-check-all-targets)
    :features ,lsp-rust-analyzer-checkonsave-features

It seems that (1) lsp-rust-features is responsible for warnings about code being disabled, e.g. greying out of code covered by #[cfg(feature = "<my feature>")] with [inactive-code] warning, and module-level [unlinked-file] warnings for modules that are currently not included in the module tree, due to parent module's use <child module> being hidden behind #[cfg(feature = "<my feature>")].

However, it seems that (2) lsp-rust-analyzer-checkonsave-features is necessary for rust analyzer to actually cargo check the disabled code (e.g. in [unlinked-file] files), independent of the setting of lsp-rust-features!

So, perhaps this issue should be reopened? I think a proper resolution would amount to an ergonomic way to get the effect of changing the default features, without actually editing the Cargo.toml file. Looking at the rust-analyzer docs, there seem to be two corresponding, separate settings there, i.e. rust-analyzer.cargo.features corresponding to lsp-rust-features here, and rust-analyzer.check.features corresponding to lsp-rust-analyzer-checkonsave-features here. However, altho those settings are minimally documented, it does say that rust-analyzer.check.features "Defaults to rust-analyzer.cargo.features", so I think at minimum a faithful analog here would be for lsp-rust-analyzer-checkonsave-features to effectively default to lsp-rust-features.

I'm going to attempt to make the "check" features default to the "cargo" features and submit a PR if I can get that working ...

@ntc2
Copy link
Contributor

ntc2 commented Sep 9, 2024

I just submitted a PR fixing the bug I mentioned in my last comment.

With that PR, the edit-lsp-rust-features function from this comment works for me. And you can make it auto-reload the rust-analyzer by adding (call-interactively 'lsp-workspace-restart) at the end, i.e.

(defun edit-lsp-rust-features ()
  "Edit `lsp-rust-features' and `lsp-rust-no-default-features'.

If the first character of input is '=', then
`lsp-rust-no-default-features' is set. Multiple features are
specified by separating them with commas. The value \"all\" is
special and means \"--all-features\"."
  (interactive)
  (let ((old-features (mapconcat 'identity lsp-rust-features ",")))
    (when lsp-rust-no-default-features
      (setq old-features (concat "=" old-features)))
    (let ((new-features (read-string "Features? " old-features)))
      (setq lsp-rust-no-default-features (string-prefix-p "=" new-features))
      (if lsp-rust-no-default-features (aset new-features 0 ?,))
      (setq lsp-rust-features (vconcat (split-string new-features "," t)))))
  (call-interactively 'lsp-workspace-restart))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants