118 releases

new 0.0.202 Feb 19, 2024
0.0.199 Jan 29, 2024
0.0.190 Dec 18, 2023
0.0.187 Nov 27, 2023
0.0.44 Mar 29, 2021

#282 in Development tools

Download history 2607/week @ 2023-11-02 2499/week @ 2023-11-09 2217/week @ 2023-11-16 2539/week @ 2023-11-23 2739/week @ 2023-11-30 2371/week @ 2023-12-07 2702/week @ 2023-12-14 1546/week @ 2023-12-21 1203/week @ 2023-12-28 2274/week @ 2024-01-04 2401/week @ 2024-01-11 2448/week @ 2024-01-18 2031/week @ 2024-01-25 2125/week @ 2024-02-01 1855/week @ 2024-02-08 2460/week @ 2024-02-15

8,765 downloads per month
Used in 11 crates (via ra_ap_ide)

MIT/Apache

2MB
70K SLoC

assists crate provides a bunch of code assists, also known as code actions (in LSP) or intentions (in IntelliJ).

An assist is a micro-refactoring, which is automatically activated in certain context. For example, if the cursor is over ,, a "swap ," assist becomes available.

Assists Guidelines

Assists are the main mechanism to deliver advanced IDE features to the user, so we should pay extra attention to the UX.

The power of assists comes from their context-awareness. The main problem with IDE features is that there are a lot of them, and it's hard to teach the user what's available. Assists solve this problem nicely: 💡 signifies that something is possible, and clicking on it reveals a short list of actions. Contrast it with Emacs M-x, which just spits an infinite list of all the features.

Here are some considerations when creating a new assist:

  • It's good to preserve semantics, and it's good to keep the code compiling, but it isn't necessary. Example: "flip binary operation" might change semantics.
  • Assist shouldn't necessary make the code "better". A lot of assist come in pairs: "if let <-> match".
  • Assists should have as narrow scope as possible. Each new assists greatly improves UX for cases where the user actually invokes it, but it makes UX worse for every case where the user clicks 💡 to invoke some other assist. So, a rarely useful assist which is always applicable can be a net negative.
  • Rarely useful actions are tricky. Sometimes there are features which are clearly useful to some users, but are just noise most of the time. We don't have a good solution here, our current approach is to make this functionality available only if assist is applicable to the whole selection. Example: sort_items sorts items alphabetically. Naively, it should be available more or less everywhere, which isn't useful. So instead we only show it if the user selects the items they want to sort.
  • Consider grouping related assists together (see Assists::add_group).
  • Make assists robust. If the assist depends on results of type-inference too much, it might only fire in fully-correct code. This makes assist less useful and (worse) less predictable. The user should have a clear intuition when each particular assist is available.
  • Make small assists, which compose. Example: rather than auto-importing enums in add_missing_match_arms, we use fully-qualified names. There's a separate assist to shorten a fully-qualified name.
  • Distinguish between assists and fixits for diagnostics. Internally, fixits and assists are equivalent. They have the same "show a list + invoke a single element" workflow, and both use Assist data structure. The main difference is in the UX: while 💡 looks only at the cursor position, diagnostics squigglies and fixits are calculated for the whole file and are presented to the user eagerly. So, diagnostics should be fixable errors, while assists can be just suggestions for an alternative way to do something. If something could be a diagnostic, it should be a diagnostic. Conversely, it might be valuable to turn a diagnostic with a lot of false errors into an assist.

See also this post: https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html

Dependencies

~15–25MB
~378K SLoC