]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_assists/src/lib.rs
Merge #9939
[rust.git] / crates / ide_assists / src / lib.rs
index 21e2524276ae76bade2be61b9e70a5235081e1a0..e477be8cab855eb9acbd79072d7432741abb35aa 100644 (file)
@@ -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 @@ macro_rules! eprintln {
 };
 
 /// 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,
@@ -61,17 +117,21 @@ mod handlers {
     mod convert_into_to_from;
     mod convert_iter_for_each_to_for;
     mod convert_tuple_struct_to_named_struct;
-    mod early_return;
+    mod convert_to_guarded_return;
+    mod convert_while_to_loop;
+    mod destructure_tuple_binding;
     mod expand_glob_import;
     mod extract_function;
+    mod extract_module;
     mod extract_struct_from_enum_variant;
     mod extract_type_alias;
     mod extract_variable;
-    mod fill_match_arms;
+    mod add_missing_match_arms;
     mod fix_visibility;
     mod flip_binexpr;
     mod flip_comma;
     mod flip_trait_bound;
+    mod generate_constant;
     mod generate_default_from_enum_variant;
     mod generate_default_from_new;
     mod generate_deref;
@@ -85,7 +145,8 @@ mod handlers {
     mod generate_is_empty_from_len;
     mod generate_new;
     mod generate_setter;
-    mod infer_function_return_type;
+    mod generate_delegate_methods;
+    mod add_return_type;
     mod inline_call;
     mod inline_local_variable;
     mod introduce_named_lifetime;
@@ -95,6 +156,9 @@ mod handlers {
     mod move_bounds;
     mod move_guard;
     mod move_module_to_file;
+    mod move_to_mod_rs;
+    mod move_from_mod_rs;
+    mod promote_local_to_const;
     mod pull_assignment_up;
     mod qualify_path;
     mod raw_string;
@@ -103,10 +167,10 @@ mod handlers {
     mod remove_unused_param;
     mod reorder_fields;
     mod reorder_impl;
+    mod replace_try_expr_with_match;
     mod replace_derive_with_manual_impl;
-    mod replace_for_loop_with_for_each;
     mod replace_if_let_with_match;
-    mod replace_impl_trait_with_generic;
+    mod introduce_named_generic;
     mod replace_let_with_if_let;
     mod replace_qualified_name_with_use;
     mod replace_string_with_char;
@@ -115,36 +179,42 @@ mod handlers {
     mod toggle_ignore;
     mod unmerge_use;
     mod unwrap_block;
+    mod unwrap_result_return_type;
     mod wrap_return_type_in_result;
 
     pub(crate) fn all() -> &'static [Handler] {
         &[
             // These are alphabetic for the foolish consistency
             add_explicit_type::add_explicit_type,
+            add_missing_match_arms::add_missing_match_arms,
             add_lifetime_to_type::add_lifetime_to_type,
+            add_return_type::add_return_type,
             add_turbo_fish::add_turbo_fish,
             apply_demorgan::apply_demorgan,
             auto_import::auto_import,
             change_visibility::change_visibility,
-            convert_bool_then::convert_if_to_bool_then,
             convert_bool_then::convert_bool_then_to_if,
+            convert_bool_then::convert_if_to_bool_then,
             convert_comment_block::convert_comment_block,
             convert_integer_literal::convert_integer_literal,
             convert_into_to_from::convert_into_to_from,
             convert_iter_for_each_to_for::convert_iter_for_each_to_for,
+            convert_iter_for_each_to_for::convert_for_loop_with_for_each,
+            convert_to_guarded_return::convert_to_guarded_return,
             convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
-            early_return::convert_to_guarded_return,
+            convert_while_to_loop::convert_while_to_loop,
+            destructure_tuple_binding::destructure_tuple_binding,
             expand_glob_import::expand_glob_import,
             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
             extract_type_alias::extract_type_alias,
-            fill_match_arms::fill_match_arms,
             fix_visibility::fix_visibility,
             flip_binexpr::flip_binexpr,
             flip_comma::flip_comma,
             flip_trait_bound::flip_trait_bound,
+            generate_constant::generate_constant,
             generate_default_from_enum_variant::generate_default_from_enum_variant,
             generate_default_from_new::generate_default_from_new,
-            generate_is_empty_from_len::generate_is_empty_from_len,
+            generate_delegate_methods::generate_delegate_methods,
             generate_deref::generate_deref,
             generate_derive::generate_derive,
             generate_enum_is_method::generate_enum_is_method,
@@ -152,14 +222,13 @@ pub(crate) fn all() -> &'static [Handler] {
             generate_enum_projection_method::generate_enum_try_into_method,
             generate_from_impl_for_enum::generate_from_impl_for_enum,
             generate_function::generate_function,
-            generate_getter::generate_getter,
-            generate_getter::generate_getter_mut,
             generate_impl::generate_impl,
+            generate_is_empty_from_len::generate_is_empty_from_len,
             generate_new::generate_new,
-            generate_setter::generate_setter,
-            infer_function_return_type::infer_function_return_type,
             inline_call::inline_call,
+            inline_call::inline_into_callers,
             inline_local_variable::inline_local_variable,
+            introduce_named_generic::introduce_named_generic,
             introduce_named_lifetime::introduce_named_lifetime,
             invert_if::invert_if,
             merge_imports::merge_imports,
@@ -168,7 +237,10 @@ pub(crate) fn all() -> &'static [Handler] {
             move_guard::move_arm_cond_to_match_guard,
             move_guard::move_guard_to_arm_body,
             move_module_to_file::move_module_to_file,
+            move_to_mod_rs::move_to_mod_rs,
+            move_from_mod_rs::move_from_mod_rs,
             pull_assignment_up::pull_assignment_up,
+            promote_local_to_const::promote_local_to_const,
             qualify_path::qualify_path,
             raw_string::add_hash,
             raw_string::make_usual_string,
@@ -178,11 +250,10 @@ pub(crate) fn all() -> &'static [Handler] {
             remove_unused_param::remove_unused_param,
             reorder_fields::reorder_fields,
             reorder_impl::reorder_impl,
+            replace_try_expr_with_match::replace_try_expr_with_match,
             replace_derive_with_manual_impl::replace_derive_with_manual_impl,
-            replace_for_loop_with_for_each::replace_for_loop_with_for_each,
             replace_if_let_with_match::replace_if_let_with_match,
             replace_if_let_with_match::replace_match_with_if_let,
-            replace_impl_trait_with_generic::replace_impl_trait_with_generic,
             replace_let_with_if_let::replace_let_with_if_let,
             replace_qualified_name_with_use::replace_qualified_name_with_use,
             sort_items::sort_items,
@@ -190,6 +261,7 @@ pub(crate) fn all() -> &'static [Handler] {
             toggle_ignore::toggle_ignore,
             unmerge_use::unmerge_use,
             unwrap_block::unwrap_block,
+            unwrap_result_return_type::unwrap_result_return_type,
             wrap_return_type_in_result::wrap_return_type_in_result,
             // These are manually sorted for better priorities. By default,
             // priority is determined by the size of the target range (smaller
@@ -204,6 +276,11 @@ pub(crate) fn all() -> &'static [Handler] {
             //
             extract_variable::extract_variable,
             extract_function::extract_function,
+            extract_module::extract_module,
+            //
+            generate_getter::generate_getter,
+            generate_getter::generate_getter_mut,
+            generate_setter::generate_setter,
             // Are you sure you want to add new assist here, and not to the
             // sorted list above?
         ]