diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 7c074a4f632..b4fb5c190f7 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs @@ -1,10 +1,63 @@ -//! `assists` crate provides a bunch of code assists, also known as code -//! actions (in LSP) or intentions (in IntelliJ). +//! `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 to +//! 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 `fill_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> #[allow(unused)] macro_rules! eprintln { ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; @@ -28,6 +81,9 @@ pub use ide_db::assists::{ }; /// Return all the assists applicable at the given position. +/// +// NOTE: We don't have a `Feature: ` section for assists, they are special-cased +// in the manual. pub fn assists( db: &RootDatabase, config: &AssistConfig,