aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/completion.rs3
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs300
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs23
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs21
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs6
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs10
-rw-r--r--crates/ra_ide/src/goto_definition.rs43
-rw-r--r--crates/ra_ide/src/inlay_hints.rs74
-rw-r--r--crates/ra_ide/src/marks.rs2
-rw-r--r--crates/ra_ide/src/references/rename.rs13
10 files changed, 439 insertions, 56 deletions
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 4ca0fdf4f..8bdc43b1a 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -65,7 +65,7 @@ pub(crate) fn completions(
65 let ctx = CompletionContext::new(db, position, config)?; 65 let ctx = CompletionContext::new(db, position, config)?;
66 66
67 let mut acc = Completions::default(); 67 let mut acc = Completions::default();
68 68 complete_attribute::complete_attribute(&mut acc, &ctx);
69 complete_fn_param::complete_fn_param(&mut acc, &ctx); 69 complete_fn_param::complete_fn_param(&mut acc, &ctx);
70 complete_keyword::complete_expr_keyword(&mut acc, &ctx); 70 complete_keyword::complete_expr_keyword(&mut acc, &ctx);
71 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx); 71 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
@@ -79,7 +79,6 @@ pub(crate) fn completions(
79 complete_postfix::complete_postfix(&mut acc, &ctx); 79 complete_postfix::complete_postfix(&mut acc, &ctx);
80 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 80 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
81 complete_trait_impl::complete_trait_impl(&mut acc, &ctx); 81 complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
82 complete_attribute::complete_attribute(&mut acc, &ctx);
83 82
84 Some(acc) 83 Some(acc)
85} 84}
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index 8bf952798..f17266221 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -3,25 +3,29 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use super::completion_context::CompletionContext; 6use ra_syntax::{ast, AstNode, SyntaxKind};
7use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use rustc_hash::FxHashSet;
8use ra_syntax::{ 8
9 ast::{Attr, AttrKind}, 9use crate::completion::{
10 AstNode, 10 completion_context::CompletionContext,
11 completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
11}; 12};
12 13
13pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { 14pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
14 if !ctx.is_attribute { 15 let attribute = ctx.attribute_under_caret.as_ref()?;
15 return;
16 }
17 16
18 let is_inner = ctx 17 match (attribute.path(), attribute.input()) {
19 .original_token 18 (Some(path), Some(ast::AttrInput::TokenTree(token_tree)))
20 .ancestors() 19 if path.to_string() == "derive" =>
21 .find_map(Attr::cast) 20 {
22 .map(|attr| attr.kind() == AttrKind::Inner) 21 complete_derive(acc, ctx, token_tree)
23 .unwrap_or(false); 22 }
23 _ => complete_attribute_start(acc, ctx, attribute),
24 }
25 Some(())
26}
24 27
28fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
25 for attr_completion in ATTRIBUTES { 29 for attr_completion in ATTRIBUTES {
26 let mut item = CompletionItem::new( 30 let mut item = CompletionItem::new(
27 CompletionKind::Attribute, 31 CompletionKind::Attribute,
@@ -37,7 +41,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
37 _ => {} 41 _ => {}
38 } 42 }
39 43
40 if is_inner || !attr_completion.should_be_inner { 44 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.should_be_inner {
41 acc.add(item); 45 acc.add(item);
42 } 46 }
43 } 47 }
@@ -126,6 +130,106 @@ const ATTRIBUTES: &[AttrCompletion] = &[
126 }, 130 },
127]; 131];
128 132
133fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
134 if let Ok(existing_derives) = parse_derive_input(derive_input) {
135 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
136 .into_iter()
137 .filter(|completion| !existing_derives.contains(completion.label))
138 {
139 let mut label = derive_completion.label.to_owned();
140 for dependency in derive_completion
141 .dependencies
142 .into_iter()
143 .filter(|&&dependency| !existing_derives.contains(dependency))
144 {
145 label.push_str(", ");
146 label.push_str(dependency);
147 }
148 acc.add(
149 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
150 .kind(CompletionItemKind::Attribute),
151 );
152 }
153
154 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
155 acc.add(
156 CompletionItem::new(
157 CompletionKind::Attribute,
158 ctx.source_range(),
159 custom_derive_name,
160 )
161 .kind(CompletionItemKind::Attribute),
162 );
163 }
164 }
165}
166
167fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
168 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
169 (Some(left_paren), Some(right_paren))
170 if left_paren.kind() == SyntaxKind::L_PAREN
171 && right_paren.kind() == SyntaxKind::R_PAREN =>
172 {
173 let mut input_derives = FxHashSet::default();
174 let mut current_derive = String::new();
175 for token in derive_input
176 .syntax()
177 .children_with_tokens()
178 .filter_map(|token| token.into_token())
179 .skip_while(|token| token != &left_paren)
180 .skip(1)
181 .take_while(|token| token != &right_paren)
182 {
183 if SyntaxKind::COMMA == token.kind() {
184 if !current_derive.is_empty() {
185 input_derives.insert(current_derive);
186 current_derive = String::new();
187 }
188 } else {
189 current_derive.push_str(token.to_string().trim());
190 }
191 }
192
193 if !current_derive.is_empty() {
194 input_derives.insert(current_derive);
195 }
196 Ok(input_derives)
197 }
198 _ => Err(()),
199 }
200}
201
202fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
203 let mut result = FxHashSet::default();
204 ctx.scope().process_all_names(&mut |name, scope_def| {
205 if let hir::ScopeDef::MacroDef(mac) = scope_def {
206 if mac.is_derive_macro() {
207 result.insert(name.to_string());
208 }
209 }
210 });
211 result
212}
213
214struct DeriveCompletion {
215 label: &'static str,
216 dependencies: &'static [&'static str],
217}
218
219/// Standard Rust derives and the information about their dependencies
220/// (the dependencies are needed so that the main derive don't break the compilation when added)
221const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
222 DeriveCompletion { label: "Clone", dependencies: &[] },
223 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
224 DeriveCompletion { label: "Debug", dependencies: &[] },
225 DeriveCompletion { label: "Default", dependencies: &[] },
226 DeriveCompletion { label: "Hash", dependencies: &[] },
227 DeriveCompletion { label: "PartialEq", dependencies: &[] },
228 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
229 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
230 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
231];
232
129#[cfg(test)] 233#[cfg(test)]
130mod tests { 234mod tests {
131 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 235 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
@@ -136,6 +240,170 @@ mod tests {
136 } 240 }
137 241
138 #[test] 242 #[test]
243 fn empty_derive_completion() {
244 assert_debug_snapshot!(
245 do_attr_completion(
246 r"
247 #[derive(<|>)]
248 struct Test {}
249 ",
250 ),
251 @r###"
252 [
253 CompletionItem {
254 label: "Clone",
255 source_range: 30..30,
256 delete: 30..30,
257 insert: "Clone",
258 kind: Attribute,
259 },
260 CompletionItem {
261 label: "Copy, Clone",
262 source_range: 30..30,
263 delete: 30..30,
264 insert: "Copy, Clone",
265 kind: Attribute,
266 },
267 CompletionItem {
268 label: "Debug",
269 source_range: 30..30,
270 delete: 30..30,
271 insert: "Debug",
272 kind: Attribute,
273 },
274 CompletionItem {
275 label: "Default",
276 source_range: 30..30,
277 delete: 30..30,
278 insert: "Default",
279 kind: Attribute,
280 },
281 CompletionItem {
282 label: "Eq, PartialEq",
283 source_range: 30..30,
284 delete: 30..30,
285 insert: "Eq, PartialEq",
286 kind: Attribute,
287 },
288 CompletionItem {
289 label: "Hash",
290 source_range: 30..30,
291 delete: 30..30,
292 insert: "Hash",
293 kind: Attribute,
294 },
295 CompletionItem {
296 label: "Ord, PartialOrd, Eq, PartialEq",
297 source_range: 30..30,
298 delete: 30..30,
299 insert: "Ord, PartialOrd, Eq, PartialEq",
300 kind: Attribute,
301 },
302 CompletionItem {
303 label: "PartialEq",
304 source_range: 30..30,
305 delete: 30..30,
306 insert: "PartialEq",
307 kind: Attribute,
308 },
309 CompletionItem {
310 label: "PartialOrd, PartialEq",
311 source_range: 30..30,
312 delete: 30..30,
313 insert: "PartialOrd, PartialEq",
314 kind: Attribute,
315 },
316 ]
317 "###
318 );
319 }
320
321 #[test]
322 fn no_completion_for_incorrect_derive() {
323 assert_debug_snapshot!(
324 do_attr_completion(
325 r"
326 #[derive{<|>)]
327 struct Test {}
328 ",
329 ),
330 @"[]"
331 );
332 }
333
334 #[test]
335 fn derive_with_input_completion() {
336 assert_debug_snapshot!(
337 do_attr_completion(
338 r"
339 #[derive(serde::Serialize, PartialEq, <|>)]
340 struct Test {}
341 ",
342 ),
343 @r###"
344 [
345 CompletionItem {
346 label: "Clone",
347 source_range: 59..59,
348 delete: 59..59,
349 insert: "Clone",
350 kind: Attribute,
351 },
352 CompletionItem {
353 label: "Copy, Clone",
354 source_range: 59..59,
355 delete: 59..59,
356 insert: "Copy, Clone",
357 kind: Attribute,
358 },
359 CompletionItem {
360 label: "Debug",
361 source_range: 59..59,
362 delete: 59..59,
363 insert: "Debug",
364 kind: Attribute,
365 },
366 CompletionItem {
367 label: "Default",
368 source_range: 59..59,
369 delete: 59..59,
370 insert: "Default",
371 kind: Attribute,
372 },
373 CompletionItem {
374 label: "Eq",
375 source_range: 59..59,
376 delete: 59..59,
377 insert: "Eq",
378 kind: Attribute,
379 },
380 CompletionItem {
381 label: "Hash",
382 source_range: 59..59,
383 delete: 59..59,
384 insert: "Hash",
385 kind: Attribute,
386 },
387 CompletionItem {
388 label: "Ord, PartialOrd, Eq",
389 source_range: 59..59,
390 delete: 59..59,
391 insert: "Ord, PartialOrd, Eq",
392 kind: Attribute,
393 },
394 CompletionItem {
395 label: "PartialOrd",
396 source_range: 59..59,
397 delete: 59..59,
398 insert: "PartialOrd",
399 kind: Attribute,
400 },
401 ]
402 "###
403 );
404 }
405
406 #[test]
139 fn test_attribute_completion() { 407 fn test_attribute_completion() {
140 assert_debug_snapshot!( 408 assert_debug_snapshot!(
141 do_attr_completion( 409 do_attr_completion(
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index aa56a5cd8..d9ea92ef8 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -2,16 +2,21 @@
2 2
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use ra_syntax::AstNode; 4use ra_syntax::AstNode;
5use rustc_hash::FxHashSet;
5use test_utils::tested_by; 6use test_utils::tested_by;
6 7
7use crate::completion::{CompletionContext, Completions}; 8use crate::completion::{CompletionContext, Completions};
8use rustc_hash::FxHashSet;
9 9
10pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 let path = match &ctx.path_prefix { 11 let path = match &ctx.path_prefix {
12 Some(path) => path.clone(), 12 Some(path) => path.clone(),
13 _ => return, 13 None => return,
14 }; 14 };
15
16 if ctx.attribute_under_caret.is_some() {
17 return;
18 }
19
15 let scope = ctx.scope(); 20 let scope = ctx.scope();
16 let context_module = scope.module(); 21 let context_module = scope.module();
17 22
@@ -1325,4 +1330,18 @@ mod tests {
1325 "### 1330 "###
1326 ); 1331 );
1327 } 1332 }
1333
1334 #[test]
1335 fn dont_complete_attr() {
1336 assert_debug_snapshot!(
1337 do_reference_completion(
1338 r"
1339 mod foo { pub struct Foo; }
1340 #[foo::<|>]
1341 fn f() {}
1342 "
1343 ),
1344 @r###"[]"###
1345 )
1346 }
1328} 1347}
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index a6a5568de..bd40af1cb 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -8,9 +8,12 @@ use hir::{Adt, ModuleDef, Type};
8use ra_syntax::AstNode; 8use ra_syntax::AstNode;
9 9
10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const) 11 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
12 || ctx.record_lit_syntax.is_some() 12 return;
13 }
14 if ctx.record_lit_syntax.is_some()
13 || ctx.record_pat_syntax.is_some() 15 || ctx.record_pat_syntax.is_some()
16 || ctx.attribute_under_caret.is_some()
14 { 17 {
15 return; 18 return;
16 } 19 }
@@ -1369,4 +1372,18 @@ mod tests {
1369 "### 1372 "###
1370 ) 1373 )
1371 } 1374 }
1375
1376 #[test]
1377 fn dont_complete_attr() {
1378 assert_debug_snapshot!(
1379 do_reference_completion(
1380 r"
1381 struct Foo;
1382 #[<|>]
1383 fn f() {}
1384 "
1385 ),
1386 @r###"[]"###
1387 )
1388 }
1372} 1389}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index c529752d4..dd87bd119 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -58,7 +58,7 @@ pub(crate) struct CompletionContext<'a> {
58 pub(super) is_macro_call: bool, 58 pub(super) is_macro_call: bool,
59 pub(super) is_path_type: bool, 59 pub(super) is_path_type: bool,
60 pub(super) has_type_args: bool, 60 pub(super) has_type_args: bool,
61 pub(super) is_attribute: bool, 61 pub(super) attribute_under_caret: Option<ast::Attr>,
62} 62}
63 63
64impl<'a> CompletionContext<'a> { 64impl<'a> CompletionContext<'a> {
@@ -116,7 +116,7 @@ impl<'a> CompletionContext<'a> {
116 is_path_type: false, 116 is_path_type: false,
117 has_type_args: false, 117 has_type_args: false,
118 dot_receiver_is_ambiguous_float_literal: false, 118 dot_receiver_is_ambiguous_float_literal: false,
119 is_attribute: false, 119 attribute_under_caret: None,
120 }; 120 };
121 121
122 let mut original_file = original_file.syntax().clone(); 122 let mut original_file = original_file.syntax().clone();
@@ -200,6 +200,7 @@ impl<'a> CompletionContext<'a> {
200 Some(ty) 200 Some(ty)
201 }) 201 })
202 .flatten(); 202 .flatten();
203 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
203 204
204 // First, let's try to complete a reference to some declaration. 205 // First, let's try to complete a reference to some declaration.
205 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 206 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
@@ -318,7 +319,6 @@ impl<'a> CompletionContext<'a> {
318 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 319 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
319 .is_some(); 320 .is_some();
320 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 321 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
321 self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some();
322 322
323 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 323 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
324 self.has_type_args = segment.type_arg_list().is_some(); 324 self.has_type_args = segment.type_arg_list().is_some();
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 914a8b471..de35c6711 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -376,16 +376,20 @@ impl ToNav for hir::Local {
376impl ToNav for hir::TypeParam { 376impl ToNav for hir::TypeParam {
377 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 377 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
378 let src = self.source(db); 378 let src = self.source(db);
379 let range = match src.value { 379 let full_range = match &src.value {
380 Either::Left(it) => it.syntax().text_range(), 380 Either::Left(it) => it.syntax().text_range(),
381 Either::Right(it) => it.syntax().text_range(), 381 Either::Right(it) => it.syntax().text_range(),
382 }; 382 };
383 let focus_range = match &src.value {
384 Either::Left(_) => None,
385 Either::Right(it) => it.name().map(|it| it.syntax().text_range()),
386 };
383 NavigationTarget { 387 NavigationTarget {
384 file_id: src.file_id.original_file(db), 388 file_id: src.file_id.original_file(db),
385 name: self.name(db).to_string().into(), 389 name: self.name(db).to_string().into(),
386 kind: TYPE_PARAM, 390 kind: TYPE_PARAM,
387 full_range: range, 391 full_range,
388 focus_range: None, 392 focus_range,
389 container_name: None, 393 container_name: None,
390 description: None, 394 description: None,
391 docs: None, 395 docs: None,
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 1dfca819d..150895abb 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -244,6 +244,39 @@ mod tests {
244 } 244 }
245 245
246 #[test] 246 #[test]
247 fn goto_def_for_use_alias() {
248 covers!(ra_ide_db::goto_def_for_use_alias);
249 check_goto(
250 "
251 //- /lib.rs
252 use foo as bar<|>;
253
254
255 //- /foo/lib.rs
256 #[macro_export]
257 macro_rules! foo { () => { () } }",
258 "SOURCE_FILE FileId(2) 0..50",
259 "#[macro_export]\nmacro_rules! foo { () => { () } }\n",
260 );
261 }
262
263 #[test]
264 fn goto_def_for_use_alias_foo_macro() {
265 check_goto(
266 "
267 //- /lib.rs
268 use foo::foo as bar<|>;
269
270 //- /foo/lib.rs
271 #[macro_export]
272 macro_rules! foo { () => { () } }
273 ",
274 "foo MACRO_CALL FileId(2) 0..49 29..32",
275 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
276 );
277 }
278
279 #[test]
247 fn goto_def_for_macros_in_use_tree() { 280 fn goto_def_for_macros_in_use_tree() {
248 check_goto( 281 check_goto(
249 " 282 "
@@ -754,14 +787,14 @@ mod tests {
754 #[test] 787 #[test]
755 fn goto_for_type_param() { 788 fn goto_for_type_param() {
756 check_goto( 789 check_goto(
757 " 790 r#"
758 //- /lib.rs 791 //- /lib.rs
759 struct Foo<T> { 792 struct Foo<T: Clone> {
760 t: <|>T, 793 t: <|>T,
761 } 794 }
762 ", 795 "#,
763 "T TYPE_PARAM FileId(1) 11..12", 796 "T TYPE_PARAM FileId(1) 11..19 11..12",
764 "T", 797 "T: Clone|T",
765 ); 798 );
766 } 799 }
767 800
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 98483df32..b391f903a 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -9,6 +9,7 @@ use ra_syntax::{
9}; 9};
10 10
11use crate::{FileId, FunctionSignature}; 11use crate::{FileId, FunctionSignature};
12use stdx::to_lower_snake_case;
12 13
13#[derive(Clone, Debug, PartialEq, Eq)] 14#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct InlayHintsConfig { 15pub struct InlayHintsConfig {
@@ -144,7 +145,7 @@ fn get_param_name_hints(
144 .iter() 145 .iter()
145 .skip(n_params_to_skip) 146 .skip(n_params_to_skip)
146 .zip(args) 147 .zip(args)
147 .filter(|(param, arg)| should_show_param_hint(&fn_signature, param, &arg)) 148 .filter(|(param, arg)| should_show_param_name_hint(sema, &fn_signature, param, &arg))
148 .map(|(param_name, arg)| InlayHint { 149 .map(|(param_name, arg)| InlayHint {
149 range: arg.syntax().text_range(), 150 range: arg.syntax().text_range(),
150 kind: InlayKind::ParameterHint, 151 kind: InlayKind::ParameterHint,
@@ -181,7 +182,7 @@ fn get_bind_pat_hints(
181 182
182fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ty: &Type) -> bool { 183fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ty: &Type) -> bool {
183 if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { 184 if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() {
184 let pat_text = bind_pat.syntax().to_string(); 185 let pat_text = bind_pat.to_string();
185 enum_data 186 enum_data
186 .variants(db) 187 .variants(db)
187 .into_iter() 188 .into_iter()
@@ -198,7 +199,7 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
198 } 199 }
199 200
200 if let Some(Adt::Struct(s)) = pat_ty.as_adt() { 201 if let Some(Adt::Struct(s)) = pat_ty.as_adt() {
201 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.syntax().to_string() { 202 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() {
202 return true; 203 return true;
203 } 204 }
204 } 205 }
@@ -230,15 +231,16 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
230 false 231 false
231} 232}
232 233
233fn should_show_param_hint( 234fn should_show_param_name_hint(
235 sema: &Semantics<RootDatabase>,
234 fn_signature: &FunctionSignature, 236 fn_signature: &FunctionSignature,
235 param_name: &str, 237 param_name: &str,
236 argument: &ast::Expr, 238 argument: &ast::Expr,
237) -> bool { 239) -> bool {
240 let param_name = param_name.trim_start_matches('_');
238 if param_name.is_empty() 241 if param_name.is_empty()
239 || is_argument_similar_to_param(argument, param_name) 242 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_'))
240 || Some(param_name.trim_start_matches('_')) 243 || is_argument_similar_to_param_name(sema, argument, param_name)
241 == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_'))
242 { 244 {
243 return false; 245 return false;
244 } 246 }
@@ -254,20 +256,42 @@ fn should_show_param_hint(
254 parameters_len != 1 || !is_obvious_param(param_name) 256 parameters_len != 1 || !is_obvious_param(param_name)
255} 257}
256 258
257fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { 259fn is_argument_similar_to_param_name(
258 let argument_string = remove_ref(argument.clone()).syntax().to_string(); 260 sema: &Semantics<RootDatabase>,
259 let param_name = param_name.trim_start_matches('_'); 261 argument: &ast::Expr,
260 let argument_string = argument_string.trim_start_matches('_'); 262 param_name: &str,
261 argument_string.starts_with(&param_name) || argument_string.ends_with(&param_name) 263) -> bool {
264 if is_enum_name_similar_to_param_name(sema, argument, param_name) {
265 return true;
266 }
267 match get_string_representation(argument) {
268 None => false,
269 Some(repr) => {
270 let argument_string = repr.trim_start_matches('_');
271 argument_string.starts_with(param_name) || argument_string.ends_with(param_name)
272 }
273 }
274}
275
276fn is_enum_name_similar_to_param_name(
277 sema: &Semantics<RootDatabase>,
278 argument: &ast::Expr,
279 param_name: &str,
280) -> bool {
281 match sema.type_of_expr(argument).and_then(|t| t.as_adt()) {
282 Some(Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name,
283 _ => false,
284 }
262} 285}
263 286
264fn remove_ref(expr: ast::Expr) -> ast::Expr { 287fn get_string_representation(expr: &ast::Expr) -> Option<String> {
265 if let ast::Expr::RefExpr(ref_expr) = &expr { 288 match expr {
266 if let Some(inner) = ref_expr.expr() { 289 ast::Expr::MethodCallExpr(method_call_expr) => {
267 return inner; 290 Some(method_call_expr.name_ref()?.to_string())
268 } 291 }
292 ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
293 _ => Some(expr.to_string()),
269 } 294 }
270 expr
271} 295}
272 296
273fn is_obvious_param(param_name: &str) -> bool { 297fn is_obvious_param(param_name: &str) -> bool {
@@ -1073,6 +1097,12 @@ struct TestVarContainer {
1073 test_var: i32, 1097 test_var: i32,
1074} 1098}
1075 1099
1100impl TestVarContainer {
1101 fn test_var(&self) -> i32 {
1102 self.test_var
1103 }
1104}
1105
1076struct Test {} 1106struct Test {}
1077 1107
1078impl Test { 1108impl Test {
@@ -1098,10 +1128,15 @@ struct Param {}
1098fn different_order(param: &Param) {} 1128fn different_order(param: &Param) {}
1099fn different_order_mut(param: &mut Param) {} 1129fn different_order_mut(param: &mut Param) {}
1100fn has_underscore(_param: bool) {} 1130fn has_underscore(_param: bool) {}
1131fn enum_matches_param_name(completion_kind: CompletionKind) {}
1101 1132
1102fn twiddle(twiddle: bool) {} 1133fn twiddle(twiddle: bool) {}
1103fn doo(_doo: bool) {} 1134fn doo(_doo: bool) {}
1104 1135
1136enum CompletionKind {
1137 Keyword,
1138}
1139
1105fn main() { 1140fn main() {
1106 let container: TestVarContainer = TestVarContainer { test_var: 42 }; 1141 let container: TestVarContainer = TestVarContainer { test_var: 42 };
1107 let test: Test = Test {}; 1142 let test: Test = Test {};
@@ -1114,18 +1149,21 @@ fn main() {
1114 let test_var: i32 = 55; 1149 let test_var: i32 = 55;
1115 test_processed.no_hints_expected(22, test_var); 1150 test_processed.no_hints_expected(22, test_var);
1116 test_processed.no_hints_expected(33, container.test_var); 1151 test_processed.no_hints_expected(33, container.test_var);
1152 test_processed.no_hints_expected(44, container.test_var());
1117 test_processed.frob(false); 1153 test_processed.frob(false);
1118 1154
1119 twiddle(true); 1155 twiddle(true);
1120 doo(true); 1156 doo(true);
1121 1157
1122 let param_begin: Param = Param {}; 1158 let mut param_begin: Param = Param {};
1123 different_order(&param_begin); 1159 different_order(&param_begin);
1124 different_order(&mut param_begin); 1160 different_order(&mut param_begin);
1125 1161
1126 let param: bool = true; 1162 let param: bool = true;
1127 has_underscore(param); 1163 has_underscore(param);
1128 1164
1165 enum_matches_param_name(CompletionKind::Keyword);
1166
1129 let a: f64 = 7.0; 1167 let a: f64 = 7.0;
1130 let b: f64 = 4.0; 1168 let b: f64 = 4.0;
1131 let _: f64 = a.div_euclid(b); 1169 let _: f64 = a.div_euclid(b);
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs
index bea30fe2a..51ca4dde3 100644
--- a/crates/ra_ide/src/marks.rs
+++ b/crates/ra_ide/src/marks.rs
@@ -11,4 +11,6 @@ test_utils::marks!(
11 self_fulfilling_completion 11 self_fulfilling_completion
12 test_struct_field_completion_in_func_call 12 test_struct_field_completion_in_func_call
13 test_struct_field_completion_in_record_lit 13 test_struct_field_completion_in_record_lit
14 test_rename_struct_field_for_shorthand
15 test_rename_local_for_field_shorthand
14); 16);
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index fd17bc9f2..916edaef2 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -7,14 +7,13 @@ use ra_syntax::{
7 algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, 7 algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode,
8}; 8};
9use ra_text_edit::TextEdit; 9use ra_text_edit::TextEdit;
10use test_utils::tested_by;
10 11
11use crate::{ 12use crate::{
12 FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, SourceChange, 13 references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind,
13 SourceFileEdit, TextRange, 14 SourceChange, SourceFileEdit, TextRange,
14}; 15};
15 16
16use super::find_all_refs;
17
18pub(crate) fn rename( 17pub(crate) fn rename(
19 db: &RootDatabase, 18 db: &RootDatabase,
20 position: FilePosition, 19 position: FilePosition,
@@ -52,11 +51,13 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
52 let file_id = reference.file_range.file_id; 51 let file_id = reference.file_range.file_id;
53 let range = match reference.kind { 52 let range = match reference.kind {
54 ReferenceKind::FieldShorthandForField => { 53 ReferenceKind::FieldShorthandForField => {
54 tested_by!(test_rename_struct_field_for_shorthand);
55 replacement_text.push_str(new_name); 55 replacement_text.push_str(new_name);
56 replacement_text.push_str(": "); 56 replacement_text.push_str(": ");
57 TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) 57 TextRange::new(reference.file_range.range.start(), reference.file_range.range.start())
58 } 58 }
59 ReferenceKind::FieldShorthandForLocal => { 59 ReferenceKind::FieldShorthandForLocal => {
60 tested_by!(test_rename_local_for_field_shorthand);
60 replacement_text.push_str(": "); 61 replacement_text.push_str(": ");
61 replacement_text.push_str(new_name); 62 replacement_text.push_str(new_name);
62 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) 63 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
@@ -147,7 +148,7 @@ fn rename_reference(
147mod tests { 148mod tests {
148 use insta::assert_debug_snapshot; 149 use insta::assert_debug_snapshot;
149 use ra_text_edit::TextEditBuilder; 150 use ra_text_edit::TextEditBuilder;
150 use test_utils::assert_eq_text; 151 use test_utils::{assert_eq_text, covers};
151 152
152 use crate::{ 153 use crate::{
153 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, 154 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId,
@@ -379,6 +380,7 @@ mod tests {
379 380
380 #[test] 381 #[test]
381 fn test_rename_struct_field_for_shorthand() { 382 fn test_rename_struct_field_for_shorthand() {
383 covers!(test_rename_struct_field_for_shorthand);
382 test_rename( 384 test_rename(
383 r#" 385 r#"
384 struct Foo { 386 struct Foo {
@@ -408,6 +410,7 @@ mod tests {
408 410
409 #[test] 411 #[test]
410 fn test_rename_local_for_field_shorthand() { 412 fn test_rename_local_for_field_shorthand() {
413 covers!(test_rename_local_for_field_shorthand);
411 test_rename( 414 test_rename(
412 r#" 415 r#"
413 struct Foo { 416 struct Foo {