]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/lib.rs
reduce duplication
[rust.git] / crates / ide_assists / src / lib.rs
1 //! `assists` crate provides a bunch of code assists, also known as code
2 //! actions (in LSP) or intentions (in IntelliJ).
3 //!
4 //! An assist is a micro-refactoring, which is automatically activated in
5 //! certain context. For example, if the cursor is over `,`, a "swap `,`" assist
6 //! becomes available.
7
8 #[allow(unused)]
9 macro_rules! eprintln {
10     ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
11 }
12
13 mod assist_config;
14 mod assist_context;
15 #[cfg(test)]
16 mod tests;
17 pub mod utils;
18 pub mod path_transform;
19
20 use std::str::FromStr;
21
22 use hir::Semantics;
23 use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase};
24 use syntax::TextRange;
25
26 pub(crate) use crate::assist_context::{AssistContext, Assists};
27
28 pub use assist_config::AssistConfig;
29
30 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
31 pub enum AssistKind {
32     // FIXME: does the None variant make sense? Probably not.
33     None,
34
35     QuickFix,
36     Generate,
37     Refactor,
38     RefactorExtract,
39     RefactorInline,
40     RefactorRewrite,
41 }
42
43 impl AssistKind {
44     pub fn contains(self, other: AssistKind) -> bool {
45         if self == other {
46             return true;
47         }
48
49         match self {
50             AssistKind::None | AssistKind::Generate => return true,
51             AssistKind::Refactor => match other {
52                 AssistKind::RefactorExtract
53                 | AssistKind::RefactorInline
54                 | AssistKind::RefactorRewrite => return true,
55                 _ => return false,
56             },
57             _ => return false,
58         }
59     }
60
61     pub fn name(&self) -> &str {
62         match self {
63             AssistKind::None => "None",
64             AssistKind::QuickFix => "QuickFix",
65             AssistKind::Generate => "Generate",
66             AssistKind::Refactor => "Refactor",
67             AssistKind::RefactorExtract => "RefactorExtract",
68             AssistKind::RefactorInline => "RefactorInline",
69             AssistKind::RefactorRewrite => "RefactorRewrite",
70         }
71     }
72 }
73
74 impl FromStr for AssistKind {
75     type Err = String;
76
77     fn from_str(s: &str) -> Result<Self, Self::Err> {
78         match s {
79             "None" => Ok(AssistKind::None),
80             "QuickFix" => Ok(AssistKind::QuickFix),
81             "Generate" => Ok(AssistKind::Generate),
82             "Refactor" => Ok(AssistKind::Refactor),
83             "RefactorExtract" => Ok(AssistKind::RefactorExtract),
84             "RefactorInline" => Ok(AssistKind::RefactorInline),
85             "RefactorRewrite" => Ok(AssistKind::RefactorRewrite),
86             unknown => Err(format!("Unknown AssistKind: '{}'", unknown)),
87         }
88     }
89 }
90
91 /// Unique identifier of the assist, should not be shown to the user
92 /// directly.
93 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
94 pub struct AssistId(pub &'static str, pub AssistKind);
95
96 /// A way to control how many asssist to resolve during the assist resolution.
97 /// When an assist is resolved, its edits are calculated that might be costly to always do by default.
98 #[derive(Debug)]
99 pub enum AssistResolveStrategy {
100     /// No assists should be resolved.
101     None,
102     /// All assists should be resolved.
103     All,
104     /// Only a certain assist should be resolved.
105     Single(SingleResolve),
106 }
107
108 /// Hold the [`AssistId`] data of a certain assist to resolve.
109 /// The original id object cannot be used due to a `'static` lifetime
110 /// and the requirement to construct this struct dynamically during the resolve handling.
111 #[derive(Debug)]
112 pub struct SingleResolve {
113     /// The id of the assist.
114     pub assist_id: String,
115     // The kind of the assist.
116     pub assist_kind: AssistKind,
117 }
118
119 impl AssistResolveStrategy {
120     pub fn should_resolve(&self, id: &AssistId) -> bool {
121         match self {
122             AssistResolveStrategy::None => false,
123             AssistResolveStrategy::All => true,
124             AssistResolveStrategy::Single(single_resolve) => {
125                 single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1
126             }
127         }
128     }
129 }
130
131 #[derive(Clone, Debug)]
132 pub struct GroupLabel(pub String);
133
134 #[derive(Debug, Clone)]
135 pub struct Assist {
136     pub id: AssistId,
137     /// Short description of the assist, as shown in the UI.
138     pub label: Label,
139     pub group: Option<GroupLabel>,
140     /// Target ranges are used to sort assists: the smaller the target range,
141     /// the more specific assist is, and so it should be sorted first.
142     pub target: TextRange,
143     /// Computing source change sometimes is much more costly then computing the
144     /// other fields. Additionally, the actual change is not required to show
145     /// the lightbulb UI, it only is needed when the user tries to apply an
146     /// assist. So, we compute it lazily: the API allow requesting assists with
147     /// or without source change. We could (and in fact, used to) distinguish
148     /// between resolved and unresolved assists at the type level, but this is
149     /// cumbersome, especially if you want to embed an assist into another data
150     /// structure, such as a diagnostic.
151     pub source_change: Option<SourceChange>,
152 }
153
154 impl Assist {
155     /// Return all the assists applicable at the given position.
156     pub fn get(
157         db: &RootDatabase,
158         config: &AssistConfig,
159         resolve: AssistResolveStrategy,
160         range: FileRange,
161     ) -> Vec<Assist> {
162         let sema = Semantics::new(db);
163         let ctx = AssistContext::new(sema, config, range);
164         let mut acc = Assists::new(&ctx, resolve);
165         handlers::all().iter().for_each(|handler| {
166             handler(&mut acc, &ctx);
167         });
168         acc.finish()
169     }
170 }
171
172 mod handlers {
173     use crate::{AssistContext, Assists};
174
175     pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;
176
177     mod add_explicit_type;
178     mod add_lifetime_to_type;
179     mod add_missing_impl_members;
180     mod add_turbo_fish;
181     mod apply_demorgan;
182     mod auto_import;
183     mod change_visibility;
184     mod convert_integer_literal;
185     mod convert_comment_block;
186     mod convert_iter_for_each_to_for;
187     mod convert_into_to_from;
188     mod convert_tuple_struct_to_named_struct;
189     mod early_return;
190     mod expand_glob_import;
191     mod extract_function;
192     mod extract_struct_from_enum_variant;
193     mod extract_type_alias;
194     mod extract_variable;
195     mod fill_match_arms;
196     mod fix_visibility;
197     mod flip_binexpr;
198     mod flip_comma;
199     mod flip_trait_bound;
200     mod generate_default_from_enum_variant;
201     mod generate_default_from_new;
202     mod generate_is_empty_from_len;
203     mod generate_deref;
204     mod generate_derive;
205     mod generate_enum_is_method;
206     mod generate_enum_projection_method;
207     mod generate_from_impl_for_enum;
208     mod generate_function;
209     mod generate_getter;
210     mod generate_impl;
211     mod generate_new;
212     mod generate_setter;
213     mod infer_function_return_type;
214     mod inline_function;
215     mod inline_local_variable;
216     mod introduce_named_lifetime;
217     mod invert_if;
218     mod merge_imports;
219     mod merge_match_arms;
220     mod move_bounds;
221     mod move_guard;
222     mod move_module_to_file;
223     mod pull_assignment_up;
224     mod qualify_path;
225     mod raw_string;
226     mod remove_dbg;
227     mod remove_mut;
228     mod remove_unused_param;
229     mod reorder_fields;
230     mod reorder_impl;
231     mod replace_derive_with_manual_impl;
232     mod replace_for_loop_with_for_each;
233     mod replace_if_let_with_match;
234     mod replace_impl_trait_with_generic;
235     mod replace_let_with_if_let;
236     mod replace_qualified_name_with_use;
237     mod replace_string_with_char;
238     mod replace_unwrap_with_match;
239     mod split_import;
240     mod toggle_ignore;
241     mod unmerge_use;
242     mod unwrap_block;
243     mod wrap_return_type_in_result;
244
245     pub(crate) fn all() -> &'static [Handler] {
246         &[
247             // These are alphabetic for the foolish consistency
248             add_explicit_type::add_explicit_type,
249             add_lifetime_to_type::add_lifetime_to_type,
250             add_turbo_fish::add_turbo_fish,
251             apply_demorgan::apply_demorgan,
252             auto_import::auto_import,
253             change_visibility::change_visibility,
254             convert_integer_literal::convert_integer_literal,
255             convert_comment_block::convert_comment_block,
256             convert_iter_for_each_to_for::convert_iter_for_each_to_for,
257             convert_into_to_from::convert_into_to_from,
258             convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
259             early_return::convert_to_guarded_return,
260             expand_glob_import::expand_glob_import,
261             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
262             extract_type_alias::extract_type_alias,
263             fill_match_arms::fill_match_arms,
264             fix_visibility::fix_visibility,
265             flip_binexpr::flip_binexpr,
266             flip_comma::flip_comma,
267             flip_trait_bound::flip_trait_bound,
268             generate_default_from_enum_variant::generate_default_from_enum_variant,
269             generate_default_from_new::generate_default_from_new,
270             generate_is_empty_from_len::generate_is_empty_from_len,
271             generate_deref::generate_deref,
272             generate_derive::generate_derive,
273             generate_enum_is_method::generate_enum_is_method,
274             generate_enum_projection_method::generate_enum_as_method,
275             generate_enum_projection_method::generate_enum_try_into_method,
276             generate_from_impl_for_enum::generate_from_impl_for_enum,
277             generate_function::generate_function,
278             generate_getter::generate_getter,
279             generate_getter::generate_getter_mut,
280             generate_impl::generate_impl,
281             generate_new::generate_new,
282             generate_setter::generate_setter,
283             infer_function_return_type::infer_function_return_type,
284             inline_function::inline_function,
285             inline_local_variable::inline_local_variable,
286             introduce_named_lifetime::introduce_named_lifetime,
287             invert_if::invert_if,
288             merge_imports::merge_imports,
289             merge_match_arms::merge_match_arms,
290             move_bounds::move_bounds_to_where_clause,
291             move_guard::move_arm_cond_to_match_guard,
292             move_guard::move_guard_to_arm_body,
293             move_module_to_file::move_module_to_file,
294             pull_assignment_up::pull_assignment_up,
295             qualify_path::qualify_path,
296             raw_string::add_hash,
297             raw_string::make_usual_string,
298             raw_string::remove_hash,
299             remove_dbg::remove_dbg,
300             remove_mut::remove_mut,
301             remove_unused_param::remove_unused_param,
302             reorder_fields::reorder_fields,
303             reorder_impl::reorder_impl,
304             replace_derive_with_manual_impl::replace_derive_with_manual_impl,
305             replace_for_loop_with_for_each::replace_for_loop_with_for_each,
306             replace_if_let_with_match::replace_if_let_with_match,
307             replace_if_let_with_match::replace_match_with_if_let,
308             replace_impl_trait_with_generic::replace_impl_trait_with_generic,
309             replace_let_with_if_let::replace_let_with_if_let,
310             replace_qualified_name_with_use::replace_qualified_name_with_use,
311             replace_unwrap_with_match::replace_unwrap_with_match,
312             split_import::split_import,
313             toggle_ignore::toggle_ignore,
314             unmerge_use::unmerge_use,
315             unwrap_block::unwrap_block,
316             wrap_return_type_in_result::wrap_return_type_in_result,
317             // These are manually sorted for better priorities. By default,
318             // priority is determined by the size of the target range (smaller
319             // target wins). If the ranges are equal, position in this list is
320             // used as a tie-breaker.
321             add_missing_impl_members::add_missing_impl_members,
322             add_missing_impl_members::add_missing_default_members,
323             //
324             replace_string_with_char::replace_string_with_char,
325             raw_string::make_raw_string,
326             //
327             extract_variable::extract_variable,
328             extract_function::extract_function,
329             // Are you sure you want to add new assist here, and not to the
330             // sorted list above?
331         ]
332     }
333 }